xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34242)
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*34242Sbostic static char sccsid[] = "@(#)vfprintf.c	5.6 (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 
22*34242Sbostic #define	MAXBUF		120
23*34242Sbostic #define	DEFPREC		6
2434226Sbostic 
2534236Sbostic #define	PUTC(ch, fd)	{++cnt; putc(ch, fd);}
2634236Sbostic 
27*34242Sbostic #define	EFORMAT		1
28*34242Sbostic #define	FFORMAT		2
29*34242Sbostic #define	GFORMAT		3
30*34242Sbostic 
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;
48*34242Sbostic 	char argsize, printsign, *_cvt(), buf[MAXBUF];
49*34242Sbostic 	int alternate, cnt, n, ladjust, width, prec, size;
5034226Sbostic 
5134233Sbostic 	for (cnt = 0; *fmt; ++fmt) {
5234233Sbostic 		if (*fmt != '%') {
5334235Sbostic 			PUTC(*fmt, fp);
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 '%':			/* "%#%" prints as "%" */
6734235Sbostic 			PUTC('%', fp);
6834233Sbostic 			continue;
6934233Sbostic 		case '*':
7034235Sbostic 			/*
7134235Sbostic 			 * ``A negative field width argument is taken as a
7234235Sbostic 			 * - flag followed by a  positive field width.''
7334235Sbostic 			 *	-- ANSI X3J11
7434235Sbostic 			 * They don't exclude field widths read from args.
7534235Sbostic 			 */
7634235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
7734235Sbostic 				goto flags;
7834235Sbostic 			width = -width;
7934235Sbostic 			/*FALLTHROUGH*/
8034235Sbostic 		case '-':
8134235Sbostic 			ladjust = 1;
8234233Sbostic 			goto flags;
8334233Sbostic 		case '+':
8434233Sbostic 			printsign = '+';
8534233Sbostic 			goto flags;
8634233Sbostic 		case '.':
8734235Sbostic 			if (*++fmt == '*')
8834235Sbostic 				prec = va_arg(argp, int);
8934235Sbostic 			else if (isdigit(*fmt)) {
9034235Sbostic 				prec = 0;
9134233Sbostic 				do {
9234236Sbostic 					prec = 10 * prec + *fmt - '0';
9334233Sbostic 				} while isdigit(*++fmt);
9434233Sbostic 				--fmt;
9534226Sbostic 			}
9634235Sbostic 			else {
9734235Sbostic 				prec = 0;
9834235Sbostic 				--fmt;
9934235Sbostic 				goto flags;
10034235Sbostic 			}
10134235Sbostic 			if (prec < 0)
10234235Sbostic 				prec = -1;
10334233Sbostic 			goto flags;
10434233Sbostic 		case '0':
10534233Sbostic 			padc = '0';
10634235Sbostic 			/*FALLTHROUGH*/
10734233Sbostic 		case '1': case '2': case '3': case '4':
10834233Sbostic 		case '5': case '6': case '7': case '8': case '9':
10934233Sbostic 			do {
11034236Sbostic 				width = 10 * width + *fmt - '0';
11134233Sbostic 			} while isdigit(*++fmt);
11234233Sbostic 			--fmt;
11334235Sbostic 		case 'L':
11434235Sbostic 			argsize |= LONGDBL;
11534235Sbostic 			goto flags;
11634235Sbostic 		case 'h':
11734235Sbostic 			argsize |= SHORTINT;
11834235Sbostic 			goto flags;
11934233Sbostic 		case 'l':
12034235Sbostic 			argsize |= LONGINT;
12134233Sbostic 			goto flags;
12234226Sbostic 		}
12334226Sbostic 
12434233Sbostic 		digs = "0123456789abcdef";
12534226Sbostic 
12634233Sbostic 		switch (*fmt) {
12734226Sbostic 		case 'c':
12834235Sbostic 			PUTC(va_arg(argp, int), fp);
12934226Sbostic 			break;
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);
147*34242Sbostic 			bp = _cvt(_double, prec, buf, EFORMAT, *fmt,
148*34242Sbostic 			    printsign);
14934236Sbostic 			goto pbuf;
15034235Sbostic 		case 'f':
15134235Sbostic 			_double = va_arg(argp, double);
152*34242Sbostic 			bp = _cvt(_double, prec, buf, FFORMAT, 'f',
153*34242Sbostic 			    printsign);
15434236Sbostic pbuf:			size = bp - buf;
15534235Sbostic 			if (size < width && !ladjust)
15634235Sbostic 				do {
15734235Sbostic 					PUTC(padc, fp);
15834235Sbostic 				} while (--width > size);
15934235Sbostic 			for (t = buf; t < bp; ++t)
16034235Sbostic 				PUTC(*t, fp);
16134235Sbostic 			for (; width > size; --width)
16234235Sbostic 				PUTC(padc, fp);
16334235Sbostic 			break;
16434241Sbostic 		case 'G':
165*34242Sbostic 		case 'g':
16634241Sbostic 			_double = va_arg(argp, double);
167*34242Sbostic 			bp = _cvt(_double, prec, buf, GFORMAT, *fmt - 2,
168*34242Sbostic 			    printsign);
169*34242Sbostic 			goto pbuf;
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);
24334226Sbostic 			break;
24434233Sbostic 		case '\0':		/* "%?" prints ?, unless ? is NULL */
24534235Sbostic 			return(ferror(fp) ? -1 : cnt);
24634226Sbostic 		default:
24734235Sbostic 			PUTC(*fmt, fp);
24834226Sbostic 		}
24934226Sbostic 	}
25034235Sbostic 	return(ferror(fp) ? -1 : cnt);
25134226Sbostic }
252*34242Sbostic 
253*34242Sbostic char *
254*34242Sbostic _cvt(number, prec, bp, format, fmtch, printsign)
255*34242Sbostic 	double number;
256*34242Sbostic 	int prec, format;
257*34242Sbostic 	register char *bp;
258*34242Sbostic 	char fmtch, printsign;
259*34242Sbostic {
260*34242Sbostic 	int sign, decpt;
261*34242Sbostic 	register char *t;
262*34242Sbostic 	register int n;
263*34242Sbostic 	double fabs();
264*34242Sbostic 	char *ecvt(), *fcvt();
265*34242Sbostic 
266*34242Sbostic 	if (prec == -1)
267*34242Sbostic 		prec = DEFPREC;
268*34242Sbostic 	t = fabs(number) < 1 ? ecvt(number, prec + 1, &decpt, &sign) :
269*34242Sbostic 	    fcvt(number, prec + 1, &decpt, &sign);
270*34242Sbostic 
271*34242Sbostic 	if (sign)
272*34242Sbostic 		*bp++ = '-';
273*34242Sbostic 	else if (printsign)
274*34242Sbostic 		*bp++ = printsign;
275*34242Sbostic 
276*34242Sbostic 	/* E format */
277*34242Sbostic 	/* use 'e' format if exponent > precision or less than -4 */
278*34242Sbostic 	if (format == EFORMAT ||
279*34242Sbostic 	    format == GFORMAT && (decpt > prec || decpt < -3)) {
280*34242Sbostic 		*bp++ = *t ? *t++ : '0';
281*34242Sbostic 		if (format != GFORMAT && prec || prec > 1) {
282*34242Sbostic 			*bp++ = '.';
283*34242Sbostic 			while(prec--)
284*34242Sbostic 				*bp++ = *t ? *t++ : '0';
285*34242Sbostic 		}
286*34242Sbostic 		if (*t && *t > '4')
287*34242Sbostic 			++bp[-1];
288*34242Sbostic 		if (format == 2) {
289*34242Sbostic 			for (; bp[-1] == '0'; --bp);
290*34242Sbostic 			if (*bp == '.')
291*34242Sbostic 				--bp;
292*34242Sbostic 		}
293*34242Sbostic 		*bp++ = fmtch;
294*34242Sbostic 		if (--decpt < 0) {
295*34242Sbostic 			decpt = -decpt;
296*34242Sbostic 			*bp++ = '-';
297*34242Sbostic 		}
298*34242Sbostic 		else
299*34242Sbostic 			*bp++ = '+';
300*34242Sbostic 		*bp++ = decpt / 10 + '0';
301*34242Sbostic 		*bp++ = decpt % 10 + '0';
302*34242Sbostic 	}
303*34242Sbostic 	/* F format */
304*34242Sbostic 	else {
305*34242Sbostic 		if (decpt <= 0) {
306*34242Sbostic 			*bp++ = '0';
307*34242Sbostic 			if (prec) {
308*34242Sbostic 				*bp++ = '.';
309*34242Sbostic 				if (format == FFORMAT)
310*34242Sbostic 					while (decpt++ < 0 && prec--)
311*34242Sbostic 						*bp++ = '0';
312*34242Sbostic 				else while (decpt++ < 0)
313*34242Sbostic 					*bp++ = '0';
314*34242Sbostic 			}
315*34242Sbostic 		}
316*34242Sbostic 		else {
317*34242Sbostic 			for (n = 1; n <= decpt; n++)
318*34242Sbostic 				*bp++ = *t++;
319*34242Sbostic 			if (prec)
320*34242Sbostic 				*bp++ = '.';
321*34242Sbostic 		}
322*34242Sbostic 		for (n = 1; n <= prec; n++)
323*34242Sbostic 			*bp++ = *t ? *t++ : '0';
324*34242Sbostic 		if (format == GFORMAT) {
325*34242Sbostic 			for (; bp[-1] == '0'; --bp);
326*34242Sbostic 			if (bp[-1] == '.')
327*34242Sbostic 				--bp;
328*34242Sbostic 		}
329*34242Sbostic 	}
330*34242Sbostic 	return(bp);
331*34242Sbostic }
332