xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34263)
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*34263Sbostic static char sccsid[] = "@(#)vfprintf.c	5.10 (Berkeley) 05/11/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 
2234261Sbostic #define	MAXBUF	120
2334261Sbostic #define	DEFPREC	6
2434226Sbostic 
25*34263Sbostic #define	PUTC(ch)	{++cnt; putc(ch, fp);}
2634236Sbostic 
2734235Sbostic #define	LONGINT		0x01
2834235Sbostic #define	LONGDBL		0x02
2934235Sbostic #define	SHORTINT	0x04
3034241Sbostic #define	GETARG(r) \
3134241Sbostic 	r = argsize&LONGINT ? va_arg(argp, long) : \
3234241Sbostic 	    argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
3334235Sbostic 
3434248Sbostic static int alternate;
3534248Sbostic static char printsign;
3634248Sbostic 
3734235Sbostic x_doprnt(fmt, argp, fp)
3834233Sbostic 	register char *fmt;
3934233Sbostic 	va_list argp;
4034235Sbostic 	register FILE *fp;
4134226Sbostic {
42*34263Sbostic 	register int base, cnt;
43*34263Sbostic 	register char *bp, *t;
4434233Sbostic 	register u_long reg_ulong;
4534233Sbostic 	register long reg_long;
4634235Sbostic 	double _double;
47*34263Sbostic 	char argsize, padc, *_cvt(), *digs, buf[MAXBUF];
48*34263Sbostic 	int n, ladjust, width, prec, size;
4934226Sbostic 
5034243Sbostic 	digs = "0123456789abcdef";
5134233Sbostic 	for (cnt = 0; *fmt; ++fmt) {
5234233Sbostic 		if (*fmt != '%') {
53*34263Sbostic 			PUTC(*fmt);
5434233Sbostic 			continue;
5534226Sbostic 		}
5634226Sbostic 
5734235Sbostic 		alternate = ladjust = width = 0;
5834233Sbostic 		prec = -1;
5934233Sbostic 		padc = ' ';
6034235Sbostic 		argsize = printsign = '\0';
6134226Sbostic 
6234233Sbostic flags:		switch (*++fmt) {
6334233Sbostic 		case '#':
6434233Sbostic 			alternate = 1;
6534233Sbostic 			goto flags;
6634233Sbostic 		case '*':
6734235Sbostic 			/*
6834235Sbostic 			 * ``A negative field width argument is taken as a
6934235Sbostic 			 * - flag followed by a  positive field width.''
7034235Sbostic 			 *	-- ANSI X3J11
7134235Sbostic 			 * They don't exclude field widths read from args.
7234235Sbostic 			 */
7334235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
7434235Sbostic 				goto flags;
7534235Sbostic 			width = -width;
7634235Sbostic 			/*FALLTHROUGH*/
7734235Sbostic 		case '-':
7834235Sbostic 			ladjust = 1;
7934233Sbostic 			goto flags;
8034233Sbostic 		case '+':
8134233Sbostic 			printsign = '+';
8234233Sbostic 			goto flags;
8334233Sbostic 		case '.':
8434235Sbostic 			if (*++fmt == '*')
8534235Sbostic 				prec = va_arg(argp, int);
8634235Sbostic 			else if (isdigit(*fmt)) {
8734235Sbostic 				prec = 0;
8834233Sbostic 				do {
8934236Sbostic 					prec = 10 * prec + *fmt - '0';
9034233Sbostic 				} while isdigit(*++fmt);
9134233Sbostic 				--fmt;
9234226Sbostic 			}
9334235Sbostic 			else {
9434235Sbostic 				prec = 0;
9534235Sbostic 				--fmt;
9634235Sbostic 				goto flags;
9734235Sbostic 			}
9834235Sbostic 			if (prec < 0)
9934235Sbostic 				prec = -1;
10034233Sbostic 			goto flags;
10134233Sbostic 		case '0':
10234233Sbostic 			padc = '0';
10334235Sbostic 			/*FALLTHROUGH*/
10434233Sbostic 		case '1': case '2': case '3': case '4':
10534233Sbostic 		case '5': case '6': case '7': case '8': case '9':
10634233Sbostic 			do {
10734236Sbostic 				width = 10 * width + *fmt - '0';
10834233Sbostic 			} while isdigit(*++fmt);
10934233Sbostic 			--fmt;
11034235Sbostic 		case 'L':
11134235Sbostic 			argsize |= LONGDBL;
11234235Sbostic 			goto flags;
11334235Sbostic 		case 'h':
11434235Sbostic 			argsize |= SHORTINT;
11534235Sbostic 			goto flags;
11634233Sbostic 		case 'l':
11734235Sbostic 			argsize |= LONGINT;
11834233Sbostic 			goto flags;
11934243Sbostic 		case 'c': {
12034243Sbostic 			char ch;
12134226Sbostic 
12234243Sbostic 			ch = va_arg(argp, int);
123*34263Sbostic 			PUTC(ch);
12434226Sbostic 			break;
12534243Sbostic 		}
12634241Sbostic 		case 'd':
12734241Sbostic 		case 'i':
12834241Sbostic 			GETARG(reg_long);
12934241Sbostic 			if (reg_long < 0) {
13034241Sbostic 				reg_ulong = -reg_long;
13134241Sbostic 				printsign = '-';
13234241Sbostic 			}
13334241Sbostic 			else {
13434241Sbostic 				reg_ulong = reg_long;
13534241Sbostic 			}
13634241Sbostic 			if (printsign)
137*34263Sbostic 				PUTC(printsign);
13834241Sbostic 			base = 10;
13934241Sbostic 			goto num1;
14034261Sbostic 		case 'e':
14134236Sbostic 		case 'E':
14234235Sbostic 		case 'f':
14334261Sbostic 		case 'g':
14434243Sbostic 		case 'G':
14534243Sbostic 			_double = va_arg(argp, double);
14634261Sbostic 			bp = _cvt(_double, prec, buf, buf + sizeof(buf), *fmt);
14734236Sbostic pbuf:			size = bp - buf;
14834235Sbostic 			if (size < width && !ladjust)
14934235Sbostic 				do {
150*34263Sbostic 					PUTC(padc);
15134235Sbostic 				} while (--width > size);
15234235Sbostic 			for (t = buf; t < bp; ++t)
153*34263Sbostic 				PUTC(*t);
15434235Sbostic 			for (; width > size; --width)
155*34263Sbostic 				PUTC(padc);
15634235Sbostic 			break;
15734235Sbostic 		case 'n':
15834235Sbostic 			*(va_arg(argp, int *)) = cnt;
15934235Sbostic 			break;
16034226Sbostic 		case 'o':
16134235Sbostic 			GETARG(reg_ulong);
16234226Sbostic 			base = 8;
16334235Sbostic 			if (!reg_ulong || !alternate)
16434235Sbostic 				goto num1;
16534235Sbostic 			bp = buf + sizeof(buf) - 1;
16634235Sbostic 			do {
16734235Sbostic 				*bp-- = digs[reg_ulong % base];
16834235Sbostic 				reg_ulong /= base;
16934235Sbostic 			} while(reg_ulong);
17034235Sbostic 			size = &buf[sizeof(buf) - 1] - bp;
17134235Sbostic 			if (size < --width && !ladjust)
17234235Sbostic 				do {
173*34263Sbostic 					PUTC(padc);
17434235Sbostic 				} while (--width > size);
175*34263Sbostic 			PUTC('0');
17634236Sbostic 			goto num2;
17734235Sbostic 		case 'p':
17834226Sbostic 		case 's':
17934235Sbostic 			if (!(bp = va_arg(argp, char *)))
18034235Sbostic 				bp = "(null)";
18134235Sbostic 			if (width > 0 && !ladjust) {
18234233Sbostic 				char *savep;
18334226Sbostic 
18434235Sbostic 				savep = bp;
18534235Sbostic 				for (n = 0; *bp && (prec < 0 || n < prec);
18634235Sbostic 				    n++, bp++);
18734235Sbostic 				bp = savep;
18834235Sbostic 				while (n++ < width)
189*34263Sbostic 					PUTC(' ');
19034233Sbostic 			}
19134235Sbostic 			for (n = 0; *bp; ++bp) {
19234235Sbostic 				if (++n > prec && prec >= 0)
19334226Sbostic 					break;
194*34263Sbostic 				PUTC(*bp);
19534233Sbostic 			}
19634235Sbostic 			if (n < width && ladjust)
19734233Sbostic 				do {
198*34263Sbostic 					PUTC(' ');
19934235Sbostic 				} while (++n < width);
20034226Sbostic 			break;
20134226Sbostic 		case 'u':
20234235Sbostic 			GETARG(reg_ulong);
20334226Sbostic 			base = 10;
20434235Sbostic 			goto num1;
20534226Sbostic 		case 'X':
20634226Sbostic 			digs = "0123456789ABCDEF";
20734233Sbostic 			/*FALLTHROUGH*/
20834226Sbostic 		case 'x':
20934235Sbostic 			GETARG(reg_ulong);
21034233Sbostic 			if (alternate && reg_ulong) {
211*34263Sbostic 				PUTC('0');
212*34263Sbostic 				PUTC(*fmt);
21334233Sbostic 			}
21434226Sbostic 			base = 16;
21534235Sbostic num1:			bp = buf + sizeof(buf) - 1;
21634233Sbostic 			do {
21734235Sbostic 				*bp-- = digs[reg_ulong % base];
21834233Sbostic 				reg_ulong /= base;
21934233Sbostic 			} while(reg_ulong);
22034235Sbostic 			size = &buf[sizeof(buf) - 1] - bp;
22134235Sbostic 			for (; size < prec; *bp-- = '0', ++size);
22234235Sbostic 			if (size < width && !ladjust)
22334235Sbostic 				do {
224*34263Sbostic 					PUTC(padc);
22534235Sbostic 				} while (--width > size);
22634236Sbostic num2:			while (++bp != &buf[MAXBUF])
227*34263Sbostic 				PUTC(*bp);
22834235Sbostic 			for (; width > size; --width)
229*34263Sbostic 				PUTC(padc);
23034243Sbostic 			digs = "0123456789abcdef";
23134226Sbostic 			break;
23234233Sbostic 		case '\0':		/* "%?" prints ?, unless ? is NULL */
23334235Sbostic 			return(ferror(fp) ? -1 : cnt);
23434226Sbostic 		default:
235*34263Sbostic 			PUTC(*fmt);
23634226Sbostic 		}
23734226Sbostic 	}
23834235Sbostic 	return(ferror(fp) ? -1 : cnt);
23934226Sbostic }
24034242Sbostic 
24134261Sbostic #define	EFORMAT	0x01
24234261Sbostic #define	FFORMAT	0x02
24334261Sbostic #define	GFORMAT	0x04
24434261Sbostic 
24534261Sbostic static char *
24634261Sbostic _cvt(number, prec, startp, endp, fmtch)
24734242Sbostic 	double number;
24834261Sbostic 	register int prec;
24934248Sbostic 	char *startp, *endp, fmtch;
25034242Sbostic {
25134248Sbostic 	register char *p;
25234261Sbostic 	register int expcnt, format;
25334248Sbostic 	double fract, integer, tmp, modf();
25434261Sbostic 	int decpt;
25534248Sbostic 	char *savep;
25634242Sbostic 
25734248Sbostic 	if (prec == -1)				/* set default precision */
25834242Sbostic 		prec = DEFPREC;
25934242Sbostic 
26034248Sbostic 	p = endp - 1;
26134248Sbostic 	if (number < 0) {			/* set sign */
26234248Sbostic 		*startp++ = '-';
26334248Sbostic 		number = -number;
26434248Sbostic 	}
26534242Sbostic 	else if (printsign)
26634248Sbostic 		*startp++ = '+';
26734242Sbostic 
26834261Sbostic 	switch(fmtch) {
26934261Sbostic 	case 'e':
27034261Sbostic 	case 'E':
27134261Sbostic 		format = EFORMAT;
27234261Sbostic 		break;
27334261Sbostic 	case 'f':
27434261Sbostic 		format = FFORMAT;
27534261Sbostic 		break;
27634261Sbostic 	case 'g':
27734261Sbostic 	case 'G':
27834261Sbostic 		format = GFORMAT;
27934261Sbostic 		fmtch -= 2;
28034261Sbostic 	}
28134261Sbostic 
28234248Sbostic 	/*
28334248Sbostic 	 * if the alternate flag is set, or, at least one digit of precision
28434248Sbostic 	 * was requested, add a decimal point, unless it's the g/G format
28534248Sbostic 	 * in which case we require two digits of precision, since it counts
28634248Sbostic 	 * precision differently.
28734248Sbostic 	 */
28834248Sbostic 	decpt = alternate || prec > 1 || !(format&GFORMAT) && prec;
28934248Sbostic 
29034248Sbostic 	expcnt = 0;
29134248Sbostic 	fract = modf(number, &integer);
29234248Sbostic 	if (integer) {
29334248Sbostic 		register char *p2;
29434248Sbostic 
29534248Sbostic 		/* get integer part of number; count decimal places */
29634248Sbostic 		for (; integer; ++expcnt) {
29734248Sbostic 			tmp = modf(integer / 10, &integer);
29834248Sbostic 			*p-- = (int)((tmp + .03) * 10) + '0';
29934242Sbostic 		}
30034248Sbostic 
30134248Sbostic 		/* copy, in reverse order, to start of buffer */
30234248Sbostic 		p2 = startp;
30334248Sbostic 		*p2++ = *++p;
30434248Sbostic 
30534248Sbostic 		/*
30634248Sbostic 		 * if the format is g/G, and the resulting exponent will be
30734248Sbostic 		 * greater than the precision, use e/E format.  If e/E format,
30834248Sbostic 		 * put in a decimal point as needed, and decrement precision
30934248Sbostic 		 * count for each digit after the decimal point.
31034248Sbostic 		 */
31134248Sbostic 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
31234248Sbostic 			if (format&GFORMAT) {
31334248Sbostic 				format |= EFORMAT;
31434248Sbostic 
31534248Sbostic 				/* first digit is precision for g/G format */
31634248Sbostic 				if (prec)
31734248Sbostic 					--prec;
31834248Sbostic 			}
31934248Sbostic 			if (decpt)
32034248Sbostic 				*p2++ = '.';
32134248Sbostic 			for (; ++p < endp && prec; --prec, *p2++ = *p);
32234248Sbostic 
32334248Sbostic 			/* precision ran out; round number */
32434248Sbostic 			if (p < endp) {
32534248Sbostic 				if (*p > '4') {
32634248Sbostic 					for (savep = p2--;; *p2-- = '0') {
32734248Sbostic 						if (*p2 == '.')
32834248Sbostic 							--p2;
32934248Sbostic 						if (++*p2 <= '9')
33034248Sbostic 							break;
33134248Sbostic 					}
33234248Sbostic 					p2 = savep;
33334248Sbostic 				}
33434248Sbostic 				fract = 0;
33534248Sbostic 			}
33634242Sbostic 		}
33734248Sbostic 		/*
33834248Sbostic 		 * g/G in f format; if run out of precision, replace digits
33934248Sbostic 		 * with zeroes, note, have to round first, otherwise lose
34034248Sbostic 		 * rounding point.
34134248Sbostic 		 */
34234248Sbostic 		else if (format&GFORMAT) {
34334248Sbostic 			for (; ++p < endp && prec; --prec, *p2++ = *p);
34434248Sbostic 			/* precision ran out; round and then add zeroes */
34534248Sbostic 			if (p < endp) {
34634248Sbostic 				if (*p > '4') {
34734248Sbostic 					for (savep = p2--; ++*p2 > '9';
34834248Sbostic 					    *p2-- = '0');
34934248Sbostic 					p2 = savep;
35034248Sbostic 				}
35134248Sbostic 				do {
35234248Sbostic 					*p2++ = '0';
35334248Sbostic 				} while (++p < endp);
35434248Sbostic 				fract = 0;
35534248Sbostic 			}
35634248Sbostic 			if (decpt)
35734248Sbostic 				*p2++ = '.';
35834242Sbostic 		}
35934248Sbostic 		/* f format */
36034248Sbostic 		else {
36134248Sbostic 			for (; ++p < endp; *p2++ = *p);
36234248Sbostic 			if (decpt)
36334248Sbostic 				*p2++ = '.';
36434248Sbostic 		}
36534248Sbostic 		p = p2;
36634248Sbostic 	}
36734248Sbostic 	/*
36834248Sbostic 	 * it's unclear from the ANSI X3J11 spec if the g/G format should
36934248Sbostic 	 * just result in an empty string, because it's supposed to remove
37034248Sbostic 	 * trailing zeroes.  That seems counter-intuitive, so here it does
37134248Sbostic 	 * what f and e/E do; if no fraction, the number was zero, and if
37234248Sbostic 	 * no precision can't show anything after the decimal point.
37334248Sbostic 	 */
37434248Sbostic 	else if (!fract || !prec) {
37534248Sbostic 		*startp++ = '0';
37634248Sbostic 		if (decpt)
37734248Sbostic 			*startp++ = '.';
37834248Sbostic 		*startp++ = '\0';
37934248Sbostic 		return(startp);
38034248Sbostic 	}
38134248Sbostic 	/*
38234248Sbostic 	 * if the format is g/G, and the resulting exponent will be less than
38334248Sbostic 	 * -4 use e/E format.  If e/E format, compute exponent value.
38434248Sbostic 	 */
38534248Sbostic 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
38634248Sbostic 		format |= EFORMAT;
38734248Sbostic 		if (fract)
38834248Sbostic 			for (p = startp; fract;) {
38934248Sbostic 				fract = modf(fract * 10, &tmp);
39034248Sbostic 				if (!tmp) {
39134248Sbostic 					--expcnt;
39234248Sbostic 					continue;
39334248Sbostic 				}
39434248Sbostic 				*p++ = (int)tmp + '0';
39534248Sbostic 				break;
39634248Sbostic 			}
39734242Sbostic 		else
39834248Sbostic 			*p++ = '0';
39934248Sbostic 
40034248Sbostic 		/* g/G format, decrement precision for first digit */
40134248Sbostic 		if (format&GFORMAT && prec)
40234248Sbostic 			--prec;
40334248Sbostic 
40434248Sbostic 		/* add decimal after first non-zero digit */
40534248Sbostic 		if (decpt)
40634248Sbostic 			*p++ = '.';
40734242Sbostic 	}
40834248Sbostic 	/*
40934248Sbostic 	 * f format or g/G printed as f format; don't worry about decimal
41034248Sbostic 	 * point, if g/G format doesn't need it, will get stripped later.
41134248Sbostic 	 */
41234242Sbostic 	else {
41334248Sbostic 		p = startp;
41434248Sbostic 		*p++ = '0';
41534248Sbostic 		*p++ = '.';
41634248Sbostic 	}
41734248Sbostic 
41834248Sbostic 	/* finish out requested precision from fractional value */
41934248Sbostic 	while (prec--)
42034248Sbostic 		if (fract) {
42134248Sbostic 			fract = modf(fract * 10, &tmp);
42234248Sbostic 			*p++ = (int)tmp + '0';
42334248Sbostic 		}
42434248Sbostic 		else
42534248Sbostic 			*p++ = '0';
42634248Sbostic 
42734248Sbostic 	/*
42834248Sbostic 	 * if any fractional value left, "round" it back up to the beginning
42934248Sbostic 	 * of the number, fixing the exponent as necessary, and avoiding the
43034248Sbostic 	 * decimal point.
43134248Sbostic 	 */
43234248Sbostic 	if (fract) {
43334248Sbostic 		(void)modf(fract * 10, &tmp);
43434248Sbostic 		if (tmp > 4) {
43534248Sbostic 			for (savep = p--;; *p-- = '0') {
43634248Sbostic 				if (*p == '.')
43734248Sbostic 					--p;
43834248Sbostic 				if (p == startp) {
43934248Sbostic 					*p = '1';
44034248Sbostic 					++expcnt;
44134248Sbostic 					break;
44234248Sbostic 				}
44334248Sbostic 				if (++*p <= '9')
44434248Sbostic 					break;
44534242Sbostic 			}
44634248Sbostic 			p = savep;
44734242Sbostic 		}
44834248Sbostic 	}
44934248Sbostic 
45034248Sbostic 	/*
45134248Sbostic 	 * if a g/G format and not alternate flag, lose trailing zeroes,
45234248Sbostic 	 * if e/E or g/G format, and last char is decimal point, lose it.
45334248Sbostic 	 */
45434248Sbostic 	if (!alternate) {
45534248Sbostic 		if (format&GFORMAT)
45634248Sbostic 			for (; p[-1] == '0'; --p);
45734248Sbostic 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
45834248Sbostic 			--p;
45934248Sbostic 	}
46034248Sbostic 
46134248Sbostic 	/* if an e/E format, add exponent */
46234248Sbostic 	if (format&EFORMAT) {
46334248Sbostic 		*p++ = fmtch;
46434248Sbostic 		if (--expcnt < 0) {
46534248Sbostic 			expcnt = -expcnt;
46634248Sbostic 			*p++ = '-';
46734242Sbostic 		}
46834248Sbostic 		else
46934248Sbostic 			*p++ = '+';
47034248Sbostic 		*p++ = expcnt / 10 + '0';
47134248Sbostic 		*p++ = expcnt % 10 + '0';
47234242Sbostic 	}
47334248Sbostic 	return(p);
47434242Sbostic }
475