xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34243)
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*34243Sbostic static char sccsid[] = "@(#)vfprintf.c	5.7 (Berkeley) 05/09/88";
1534233Sbostic #endif /* LIBC_SCCS and not lint */
1634233Sbostic 
1734233Sbostic #include <sys/param.h>
1834233Sbostic #include <varargs.h>
1934226Sbostic #include <stdio.h>
2034233Sbostic #include <ctype.h>
2134226Sbostic 
2234242Sbostic #define	MAXBUF		120
2334242Sbostic #define	DEFPREC		6
2434226Sbostic 
2534236Sbostic #define	PUTC(ch, fd)	{++cnt; putc(ch, fd);}
2634236Sbostic 
2734242Sbostic #define	EFORMAT		1
2834242Sbostic #define	FFORMAT		2
2934242Sbostic #define	GFORMAT		3
3034242Sbostic 
3134235Sbostic #define	LONGINT		0x01
3234235Sbostic #define	LONGDBL		0x02
3334235Sbostic #define	SHORTINT	0x04
3434241Sbostic #define	GETARG(r) \
3534241Sbostic 	r = argsize&LONGINT ? va_arg(argp, long) : \
3634241Sbostic 	    argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
3734235Sbostic 
3834235Sbostic x_doprnt(fmt, argp, fp)
3934233Sbostic 	register char *fmt;
4034233Sbostic 	va_list argp;
4134235Sbostic 	register FILE *fp;
4234226Sbostic {
4334233Sbostic 	register u_long reg_ulong;
4434233Sbostic 	register long reg_long;
4534233Sbostic 	register int base;
4634235Sbostic 	register char *digs, *bp, *t, padc;
4734235Sbostic 	double _double;
4834242Sbostic 	char argsize, printsign, *_cvt(), buf[MAXBUF];
4934242Sbostic 	int alternate, cnt, n, ladjust, width, prec, size;
5034226Sbostic 
51*34243Sbostic 	digs = "0123456789abcdef";
5234233Sbostic 	for (cnt = 0; *fmt; ++fmt) {
5334233Sbostic 		if (*fmt != '%') {
5434235Sbostic 			PUTC(*fmt, fp);
5534233Sbostic 			continue;
5634226Sbostic 		}
5734226Sbostic 
5834235Sbostic 		alternate = ladjust = width = 0;
5934233Sbostic 		prec = -1;
6034233Sbostic 		padc = ' ';
6134235Sbostic 		argsize = printsign = '\0';
6234226Sbostic 
6334233Sbostic flags:		switch (*++fmt) {
6434233Sbostic 		case '#':
6534233Sbostic 			alternate = 1;
6634233Sbostic 			goto flags;
6734233Sbostic 		case '*':
6834235Sbostic 			/*
6934235Sbostic 			 * ``A negative field width argument is taken as a
7034235Sbostic 			 * - flag followed by a  positive field width.''
7134235Sbostic 			 *	-- ANSI X3J11
7234235Sbostic 			 * They don't exclude field widths read from args.
7334235Sbostic 			 */
7434235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
7534235Sbostic 				goto flags;
7634235Sbostic 			width = -width;
7734235Sbostic 			/*FALLTHROUGH*/
7834235Sbostic 		case '-':
7934235Sbostic 			ladjust = 1;
8034233Sbostic 			goto flags;
8134233Sbostic 		case '+':
8234233Sbostic 			printsign = '+';
8334233Sbostic 			goto flags;
8434233Sbostic 		case '.':
8534235Sbostic 			if (*++fmt == '*')
8634235Sbostic 				prec = va_arg(argp, int);
8734235Sbostic 			else if (isdigit(*fmt)) {
8834235Sbostic 				prec = 0;
8934233Sbostic 				do {
9034236Sbostic 					prec = 10 * prec + *fmt - '0';
9134233Sbostic 				} while isdigit(*++fmt);
9234233Sbostic 				--fmt;
9334226Sbostic 			}
9434235Sbostic 			else {
9534235Sbostic 				prec = 0;
9634235Sbostic 				--fmt;
9734235Sbostic 				goto flags;
9834235Sbostic 			}
9934235Sbostic 			if (prec < 0)
10034235Sbostic 				prec = -1;
10134233Sbostic 			goto flags;
10234233Sbostic 		case '0':
10334233Sbostic 			padc = '0';
10434235Sbostic 			/*FALLTHROUGH*/
10534233Sbostic 		case '1': case '2': case '3': case '4':
10634233Sbostic 		case '5': case '6': case '7': case '8': case '9':
10734233Sbostic 			do {
10834236Sbostic 				width = 10 * width + *fmt - '0';
10934233Sbostic 			} while isdigit(*++fmt);
11034233Sbostic 			--fmt;
11134235Sbostic 		case 'L':
11234235Sbostic 			argsize |= LONGDBL;
11334235Sbostic 			goto flags;
11434235Sbostic 		case 'h':
11534235Sbostic 			argsize |= SHORTINT;
11634235Sbostic 			goto flags;
11734233Sbostic 		case 'l':
11834235Sbostic 			argsize |= LONGINT;
11934233Sbostic 			goto flags;
120*34243Sbostic 		case '%':			/* "%#%" prints as "%" */
121*34243Sbostic 			PUTC('%', fp);
122*34243Sbostic 			break;
123*34243Sbostic 		case 'c': {
124*34243Sbostic 			char ch;
12534226Sbostic 
126*34243Sbostic 			ch = va_arg(argp, int);
127*34243Sbostic 			PUTC(ch, fp);
12834226Sbostic 			break;
129*34243Sbostic 		}
13034241Sbostic 		case 'd':
13134241Sbostic 		case 'i':
13234241Sbostic 			GETARG(reg_long);
13334241Sbostic 			if (reg_long < 0) {
13434241Sbostic 				reg_ulong = -reg_long;
13534241Sbostic 				printsign = '-';
13634241Sbostic 			}
13734241Sbostic 			else {
13834241Sbostic 				reg_ulong = reg_long;
13934241Sbostic 			}
14034241Sbostic 			if (printsign)
14134241Sbostic 				PUTC(printsign, fp);
14234241Sbostic 			base = 10;
14334241Sbostic 			goto num1;
14434236Sbostic 		case 'E':
14534236Sbostic 		case 'e':
14634236Sbostic 			_double = va_arg(argp, double);
14734242Sbostic 			bp = _cvt(_double, prec, buf, EFORMAT, *fmt,
148*34243Sbostic 			    printsign, alternate);
14934236Sbostic 			goto pbuf;
15034235Sbostic 		case 'f':
15134235Sbostic 			_double = va_arg(argp, double);
15234242Sbostic 			bp = _cvt(_double, prec, buf, FFORMAT, 'f',
153*34243Sbostic 			    printsign, alternate);
154*34243Sbostic 			goto pbuf;
155*34243Sbostic 		case 'G':
156*34243Sbostic 		case 'g':
157*34243Sbostic 			_double = va_arg(argp, double);
158*34243Sbostic 			bp = _cvt(_double, prec, buf, GFORMAT, *fmt - 2,
159*34243Sbostic 			    printsign, alternate);
16034236Sbostic pbuf:			size = bp - buf;
16134235Sbostic 			if (size < width && !ladjust)
16234235Sbostic 				do {
16334235Sbostic 					PUTC(padc, fp);
16434235Sbostic 				} while (--width > size);
16534235Sbostic 			for (t = buf; t < bp; ++t)
16634235Sbostic 				PUTC(*t, fp);
16734235Sbostic 			for (; width > size; --width)
16834235Sbostic 				PUTC(padc, fp);
16934235Sbostic 			break;
17034235Sbostic 		case 'n':
17134235Sbostic 			*(va_arg(argp, int *)) = cnt;
17234235Sbostic 			break;
17334226Sbostic 		case 'o':
17434235Sbostic 			GETARG(reg_ulong);
17534226Sbostic 			base = 8;
17634235Sbostic 			if (!reg_ulong || !alternate)
17734235Sbostic 				goto num1;
17834235Sbostic 			bp = buf + sizeof(buf) - 1;
17934235Sbostic 			do {
18034235Sbostic 				*bp-- = digs[reg_ulong % base];
18134235Sbostic 				reg_ulong /= base;
18234235Sbostic 			} while(reg_ulong);
18334235Sbostic 			size = &buf[sizeof(buf) - 1] - bp;
18434235Sbostic 			if (size < --width && !ladjust)
18534235Sbostic 				do {
18634235Sbostic 					PUTC(padc, fp);
18734235Sbostic 				} while (--width > size);
18834235Sbostic 			PUTC('0', fp);
18934236Sbostic 			goto num2;
19034235Sbostic 		case 'p':
19134226Sbostic 		case 's':
19234235Sbostic 			if (!(bp = va_arg(argp, char *)))
19334235Sbostic 				bp = "(null)";
19434235Sbostic 			if (width > 0 && !ladjust) {
19534233Sbostic 				char *savep;
19634226Sbostic 
19734235Sbostic 				savep = bp;
19834235Sbostic 				for (n = 0; *bp && (prec < 0 || n < prec);
19934235Sbostic 				    n++, bp++);
20034235Sbostic 				bp = savep;
20134235Sbostic 				while (n++ < width)
20234235Sbostic 					PUTC(' ', fp);
20334233Sbostic 			}
20434235Sbostic 			for (n = 0; *bp; ++bp) {
20534235Sbostic 				if (++n > prec && prec >= 0)
20634226Sbostic 					break;
20734235Sbostic 				PUTC(*bp, fp);
20834233Sbostic 			}
20934235Sbostic 			if (n < width && ladjust)
21034233Sbostic 				do {
21134235Sbostic 					PUTC(' ', fp);
21234235Sbostic 				} while (++n < width);
21334226Sbostic 			break;
21434226Sbostic 		case 'u':
21534235Sbostic 			GETARG(reg_ulong);
21634226Sbostic 			base = 10;
21734235Sbostic 			goto num1;
21834226Sbostic 		case 'X':
21934226Sbostic 			digs = "0123456789ABCDEF";
22034233Sbostic 			/*FALLTHROUGH*/
22134226Sbostic 		case 'x':
22234235Sbostic 			GETARG(reg_ulong);
22334233Sbostic 			if (alternate && reg_ulong) {
22434235Sbostic 				PUTC('0', fp);
22534235Sbostic 				PUTC(*fmt, fp);
22634233Sbostic 			}
22734226Sbostic 			base = 16;
22834235Sbostic num1:			bp = buf + sizeof(buf) - 1;
22934233Sbostic 			do {
23034235Sbostic 				*bp-- = digs[reg_ulong % base];
23134233Sbostic 				reg_ulong /= base;
23234233Sbostic 			} while(reg_ulong);
23334235Sbostic 			size = &buf[sizeof(buf) - 1] - bp;
23434235Sbostic 			for (; size < prec; *bp-- = '0', ++size);
23534235Sbostic 			if (size < width && !ladjust)
23634235Sbostic 				do {
23734235Sbostic 					PUTC(padc, fp);
23834235Sbostic 				} while (--width > size);
23934236Sbostic num2:			while (++bp != &buf[MAXBUF])
24034235Sbostic 				PUTC(*bp, fp);
24134235Sbostic 			for (; width > size; --width)
24234235Sbostic 				PUTC(padc, fp);
243*34243Sbostic 			digs = "0123456789abcdef";
24434226Sbostic 			break;
24534233Sbostic 		case '\0':		/* "%?" prints ?, unless ? is NULL */
24634235Sbostic 			return(ferror(fp) ? -1 : cnt);
24734226Sbostic 		default:
24834235Sbostic 			PUTC(*fmt, fp);
24934226Sbostic 		}
25034226Sbostic 	}
25134235Sbostic 	return(ferror(fp) ? -1 : cnt);
25234226Sbostic }
25334242Sbostic 
25434242Sbostic char *
255*34243Sbostic _cvt(number, prec, bp, format, fmtch, printsign, alternate)
25634242Sbostic 	double number;
257*34243Sbostic 	int prec, format, alternate;
25834242Sbostic 	register char *bp;
25934242Sbostic 	char fmtch, printsign;
26034242Sbostic {
26134242Sbostic 	int sign, decpt;
26234242Sbostic 	register char *t;
26334242Sbostic 	register int n;
26434242Sbostic 	double fabs();
26534242Sbostic 	char *ecvt(), *fcvt();
26634242Sbostic 
26734242Sbostic 	if (prec == -1)
26834242Sbostic 		prec = DEFPREC;
26934242Sbostic 	t = fabs(number) < 1 ? ecvt(number, prec + 1, &decpt, &sign) :
27034242Sbostic 	    fcvt(number, prec + 1, &decpt, &sign);
27134242Sbostic 
27234242Sbostic 	if (sign)
27334242Sbostic 		*bp++ = '-';
27434242Sbostic 	else if (printsign)
27534242Sbostic 		*bp++ = printsign;
27634242Sbostic 
27734242Sbostic 	/* E format */
27834242Sbostic 	/* use 'e' format if exponent > precision or less than -4 */
27934242Sbostic 	if (format == EFORMAT ||
28034242Sbostic 	    format == GFORMAT && (decpt > prec || decpt < -3)) {
28134242Sbostic 		*bp++ = *t ? *t++ : '0';
28234242Sbostic 		if (format != GFORMAT && prec || prec > 1) {
28334242Sbostic 			*bp++ = '.';
28434242Sbostic 			while(prec--)
28534242Sbostic 				*bp++ = *t ? *t++ : '0';
28634242Sbostic 		}
287*34243Sbostic 		else if (alternate)
288*34243Sbostic 			*bp++ = '.';
28934242Sbostic 		if (*t && *t > '4')
29034242Sbostic 			++bp[-1];
291*34243Sbostic 		if (format == GFORMAT && !alternate) {
29234242Sbostic 			for (; bp[-1] == '0'; --bp);
293*34243Sbostic 			if (bp[-1] == '.')
29434242Sbostic 				--bp;
29534242Sbostic 		}
29634242Sbostic 		*bp++ = fmtch;
29734242Sbostic 		if (--decpt < 0) {
29834242Sbostic 			decpt = -decpt;
29934242Sbostic 			*bp++ = '-';
30034242Sbostic 		}
30134242Sbostic 		else
30234242Sbostic 			*bp++ = '+';
30334242Sbostic 		*bp++ = decpt / 10 + '0';
30434242Sbostic 		*bp++ = decpt % 10 + '0';
30534242Sbostic 	}
30634242Sbostic 	/* F format */
30734242Sbostic 	else {
30834242Sbostic 		if (decpt <= 0) {
30934242Sbostic 			*bp++ = '0';
31034242Sbostic 			if (prec) {
31134242Sbostic 				*bp++ = '.';
31234242Sbostic 				if (format == FFORMAT)
31334242Sbostic 					while (decpt++ < 0 && prec--)
31434242Sbostic 						*bp++ = '0';
31534242Sbostic 				else while (decpt++ < 0)
31634242Sbostic 					*bp++ = '0';
31734242Sbostic 			}
318*34243Sbostic 			else if (alternate)
319*34243Sbostic 				*bp++ = '.';
32034242Sbostic 		}
32134242Sbostic 		else {
32234242Sbostic 			for (n = 1; n <= decpt; n++)
32334242Sbostic 				*bp++ = *t++;
324*34243Sbostic 			if (prec || alternate)
32534242Sbostic 				*bp++ = '.';
32634242Sbostic 		}
32734242Sbostic 		for (n = 1; n <= prec; n++)
32834242Sbostic 			*bp++ = *t ? *t++ : '0';
329*34243Sbostic 		if (format == GFORMAT && !alternate) {
33034242Sbostic 			for (; bp[-1] == '0'; --bp);
33134242Sbostic 			if (bp[-1] == '.')
33234242Sbostic 				--bp;
33334242Sbostic 		}
33434242Sbostic 	}
33534242Sbostic 	return(bp);
33634242Sbostic }
337