xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34248)
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*34248Sbostic static char sccsid[] = "@(#)vfprintf.c	5.8 (Berkeley) 05/10/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 
27*34248Sbostic #define	EFORMAT		0x01
28*34248Sbostic #define	FFORMAT		0x02
29*34248Sbostic #define	GFORMAT		0x04
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 
38*34248Sbostic static int alternate;
39*34248Sbostic static char printsign;
40*34248Sbostic 
4134235Sbostic x_doprnt(fmt, argp, fp)
4234233Sbostic 	register char *fmt;
4334233Sbostic 	va_list argp;
4434235Sbostic 	register FILE *fp;
4534226Sbostic {
4634233Sbostic 	register u_long reg_ulong;
4734233Sbostic 	register long reg_long;
4834233Sbostic 	register int base;
4934235Sbostic 	register char *digs, *bp, *t, padc;
5034235Sbostic 	double _double;
51*34248Sbostic 	char argsize, *_cvt(), buf[MAXBUF];
52*34248Sbostic 	int cnt, n, ladjust, width, prec, size;
5334226Sbostic 
5434243Sbostic 	digs = "0123456789abcdef";
5534233Sbostic 	for (cnt = 0; *fmt; ++fmt) {
5634233Sbostic 		if (*fmt != '%') {
5734235Sbostic 			PUTC(*fmt, fp);
5834233Sbostic 			continue;
5934226Sbostic 		}
6034226Sbostic 
6134235Sbostic 		alternate = ladjust = width = 0;
6234233Sbostic 		prec = -1;
6334233Sbostic 		padc = ' ';
6434235Sbostic 		argsize = printsign = '\0';
6534226Sbostic 
6634233Sbostic flags:		switch (*++fmt) {
6734233Sbostic 		case '#':
6834233Sbostic 			alternate = 1;
6934233Sbostic 			goto flags;
7034233Sbostic 		case '*':
7134235Sbostic 			/*
7234235Sbostic 			 * ``A negative field width argument is taken as a
7334235Sbostic 			 * - flag followed by a  positive field width.''
7434235Sbostic 			 *	-- ANSI X3J11
7534235Sbostic 			 * They don't exclude field widths read from args.
7634235Sbostic 			 */
7734235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
7834235Sbostic 				goto flags;
7934235Sbostic 			width = -width;
8034235Sbostic 			/*FALLTHROUGH*/
8134235Sbostic 		case '-':
8234235Sbostic 			ladjust = 1;
8334233Sbostic 			goto flags;
8434233Sbostic 		case '+':
8534233Sbostic 			printsign = '+';
8634233Sbostic 			goto flags;
8734233Sbostic 		case '.':
8834235Sbostic 			if (*++fmt == '*')
8934235Sbostic 				prec = va_arg(argp, int);
9034235Sbostic 			else if (isdigit(*fmt)) {
9134235Sbostic 				prec = 0;
9234233Sbostic 				do {
9334236Sbostic 					prec = 10 * prec + *fmt - '0';
9434233Sbostic 				} while isdigit(*++fmt);
9534233Sbostic 				--fmt;
9634226Sbostic 			}
9734235Sbostic 			else {
9834235Sbostic 				prec = 0;
9934235Sbostic 				--fmt;
10034235Sbostic 				goto flags;
10134235Sbostic 			}
10234235Sbostic 			if (prec < 0)
10334235Sbostic 				prec = -1;
10434233Sbostic 			goto flags;
10534233Sbostic 		case '0':
10634233Sbostic 			padc = '0';
10734235Sbostic 			/*FALLTHROUGH*/
10834233Sbostic 		case '1': case '2': case '3': case '4':
10934233Sbostic 		case '5': case '6': case '7': case '8': case '9':
11034233Sbostic 			do {
11134236Sbostic 				width = 10 * width + *fmt - '0';
11234233Sbostic 			} while isdigit(*++fmt);
11334233Sbostic 			--fmt;
11434235Sbostic 		case 'L':
11534235Sbostic 			argsize |= LONGDBL;
11634235Sbostic 			goto flags;
11734235Sbostic 		case 'h':
11834235Sbostic 			argsize |= SHORTINT;
11934235Sbostic 			goto flags;
12034233Sbostic 		case 'l':
12134235Sbostic 			argsize |= LONGINT;
12234233Sbostic 			goto flags;
12334243Sbostic 		case '%':			/* "%#%" prints as "%" */
12434243Sbostic 			PUTC('%', fp);
12534243Sbostic 			break;
12634243Sbostic 		case 'c': {
12734243Sbostic 			char ch;
12834226Sbostic 
12934243Sbostic 			ch = va_arg(argp, int);
13034243Sbostic 			PUTC(ch, fp);
13134226Sbostic 			break;
13234243Sbostic 		}
13334241Sbostic 		case 'd':
13434241Sbostic 		case 'i':
13534241Sbostic 			GETARG(reg_long);
13634241Sbostic 			if (reg_long < 0) {
13734241Sbostic 				reg_ulong = -reg_long;
13834241Sbostic 				printsign = '-';
13934241Sbostic 			}
14034241Sbostic 			else {
14134241Sbostic 				reg_ulong = reg_long;
14234241Sbostic 			}
14334241Sbostic 			if (printsign)
14434241Sbostic 				PUTC(printsign, fp);
14534241Sbostic 			base = 10;
14634241Sbostic 			goto num1;
14734236Sbostic 		case 'E':
14834236Sbostic 		case 'e':
14934236Sbostic 			_double = va_arg(argp, double);
150*34248Sbostic 			bp = _cvt(_double, prec, EFORMAT, buf,
151*34248Sbostic 			    buf + sizeof(buf), *fmt);
15234236Sbostic 			goto pbuf;
15334235Sbostic 		case 'f':
15434235Sbostic 			_double = va_arg(argp, double);
155*34248Sbostic 			bp = _cvt(_double, prec, FFORMAT, buf,
156*34248Sbostic 			    buf + sizeof(buf), 'f');
15734243Sbostic 			goto pbuf;
15834243Sbostic 		case 'G':
15934243Sbostic 		case 'g':
16034243Sbostic 			_double = va_arg(argp, double);
161*34248Sbostic 			bp = _cvt(_double, prec, GFORMAT, buf,
162*34248Sbostic 			    buf + sizeof(buf), *fmt - 2);
16334236Sbostic pbuf:			size = bp - buf;
16434235Sbostic 			if (size < width && !ladjust)
16534235Sbostic 				do {
16634235Sbostic 					PUTC(padc, fp);
16734235Sbostic 				} while (--width > size);
16834235Sbostic 			for (t = buf; t < bp; ++t)
16934235Sbostic 				PUTC(*t, fp);
17034235Sbostic 			for (; width > size; --width)
17134235Sbostic 				PUTC(padc, fp);
17234235Sbostic 			break;
17334235Sbostic 		case 'n':
17434235Sbostic 			*(va_arg(argp, int *)) = cnt;
17534235Sbostic 			break;
17634226Sbostic 		case 'o':
17734235Sbostic 			GETARG(reg_ulong);
17834226Sbostic 			base = 8;
17934235Sbostic 			if (!reg_ulong || !alternate)
18034235Sbostic 				goto num1;
18134235Sbostic 			bp = buf + sizeof(buf) - 1;
18234235Sbostic 			do {
18334235Sbostic 				*bp-- = digs[reg_ulong % base];
18434235Sbostic 				reg_ulong /= base;
18534235Sbostic 			} while(reg_ulong);
18634235Sbostic 			size = &buf[sizeof(buf) - 1] - bp;
18734235Sbostic 			if (size < --width && !ladjust)
18834235Sbostic 				do {
18934235Sbostic 					PUTC(padc, fp);
19034235Sbostic 				} while (--width > size);
19134235Sbostic 			PUTC('0', fp);
19234236Sbostic 			goto num2;
19334235Sbostic 		case 'p':
19434226Sbostic 		case 's':
19534235Sbostic 			if (!(bp = va_arg(argp, char *)))
19634235Sbostic 				bp = "(null)";
19734235Sbostic 			if (width > 0 && !ladjust) {
19834233Sbostic 				char *savep;
19934226Sbostic 
20034235Sbostic 				savep = bp;
20134235Sbostic 				for (n = 0; *bp && (prec < 0 || n < prec);
20234235Sbostic 				    n++, bp++);
20334235Sbostic 				bp = savep;
20434235Sbostic 				while (n++ < width)
20534235Sbostic 					PUTC(' ', fp);
20634233Sbostic 			}
20734235Sbostic 			for (n = 0; *bp; ++bp) {
20834235Sbostic 				if (++n > prec && prec >= 0)
20934226Sbostic 					break;
21034235Sbostic 				PUTC(*bp, fp);
21134233Sbostic 			}
21234235Sbostic 			if (n < width && ladjust)
21334233Sbostic 				do {
21434235Sbostic 					PUTC(' ', fp);
21534235Sbostic 				} while (++n < width);
21634226Sbostic 			break;
21734226Sbostic 		case 'u':
21834235Sbostic 			GETARG(reg_ulong);
21934226Sbostic 			base = 10;
22034235Sbostic 			goto num1;
22134226Sbostic 		case 'X':
22234226Sbostic 			digs = "0123456789ABCDEF";
22334233Sbostic 			/*FALLTHROUGH*/
22434226Sbostic 		case 'x':
22534235Sbostic 			GETARG(reg_ulong);
22634233Sbostic 			if (alternate && reg_ulong) {
22734235Sbostic 				PUTC('0', fp);
22834235Sbostic 				PUTC(*fmt, fp);
22934233Sbostic 			}
23034226Sbostic 			base = 16;
23134235Sbostic num1:			bp = buf + sizeof(buf) - 1;
23234233Sbostic 			do {
23334235Sbostic 				*bp-- = digs[reg_ulong % base];
23434233Sbostic 				reg_ulong /= base;
23534233Sbostic 			} while(reg_ulong);
23634235Sbostic 			size = &buf[sizeof(buf) - 1] - bp;
23734235Sbostic 			for (; size < prec; *bp-- = '0', ++size);
23834235Sbostic 			if (size < width && !ladjust)
23934235Sbostic 				do {
24034235Sbostic 					PUTC(padc, fp);
24134235Sbostic 				} while (--width > size);
24234236Sbostic num2:			while (++bp != &buf[MAXBUF])
24334235Sbostic 				PUTC(*bp, fp);
24434235Sbostic 			for (; width > size; --width)
24534235Sbostic 				PUTC(padc, fp);
24634243Sbostic 			digs = "0123456789abcdef";
24734226Sbostic 			break;
24834233Sbostic 		case '\0':		/* "%?" prints ?, unless ? is NULL */
24934235Sbostic 			return(ferror(fp) ? -1 : cnt);
25034226Sbostic 		default:
25134235Sbostic 			PUTC(*fmt, fp);
25234226Sbostic 		}
25334226Sbostic 	}
25434235Sbostic 	return(ferror(fp) ? -1 : cnt);
25534226Sbostic }
25634242Sbostic 
25734242Sbostic char *
258*34248Sbostic _cvt(number, prec, format, startp, endp, fmtch)
25934242Sbostic 	double number;
260*34248Sbostic 	int prec, format;
261*34248Sbostic 	char *startp, *endp, fmtch;
26234242Sbostic {
263*34248Sbostic 	register char *p;
264*34248Sbostic 	double fract, integer, tmp, modf();
265*34248Sbostic 	int decpt, expcnt;
266*34248Sbostic 	char *savep;
26734242Sbostic 
268*34248Sbostic 	if (prec == -1)				/* set default precision */
26934242Sbostic 		prec = DEFPREC;
27034242Sbostic 
271*34248Sbostic 	p = endp - 1;
272*34248Sbostic 	if (number < 0) {			/* set sign */
273*34248Sbostic 		*startp++ = '-';
274*34248Sbostic 		number = -number;
275*34248Sbostic 	}
27634242Sbostic 	else if (printsign)
277*34248Sbostic 		*startp++ = '+';
27834242Sbostic 
279*34248Sbostic 	/*
280*34248Sbostic 	 * if the alternate flag is set, or, at least one digit of precision
281*34248Sbostic 	 * was requested, add a decimal point, unless it's the g/G format
282*34248Sbostic 	 * in which case we require two digits of precision, since it counts
283*34248Sbostic 	 * precision differently.
284*34248Sbostic 	 */
285*34248Sbostic 	decpt = alternate || prec > 1 || !(format&GFORMAT) && prec;
286*34248Sbostic 
287*34248Sbostic 	expcnt = 0;
288*34248Sbostic 	fract = modf(number, &integer);
289*34248Sbostic 	if (integer) {
290*34248Sbostic 		register char *p2;
291*34248Sbostic 
292*34248Sbostic 		/* get integer part of number; count decimal places */
293*34248Sbostic 		for (; integer; ++expcnt) {
294*34248Sbostic 			tmp = modf(integer / 10, &integer);
295*34248Sbostic 			*p-- = (int)((tmp + .03) * 10) + '0';
29634242Sbostic 		}
297*34248Sbostic 
298*34248Sbostic 		/* copy, in reverse order, to start of buffer */
299*34248Sbostic 		p2 = startp;
300*34248Sbostic 		*p2++ = *++p;
301*34248Sbostic 
302*34248Sbostic 		/*
303*34248Sbostic 		 * if the format is g/G, and the resulting exponent will be
304*34248Sbostic 		 * greater than the precision, use e/E format.  If e/E format,
305*34248Sbostic 		 * put in a decimal point as needed, and decrement precision
306*34248Sbostic 		 * count for each digit after the decimal point.
307*34248Sbostic 		 */
308*34248Sbostic 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
309*34248Sbostic 			if (format&GFORMAT) {
310*34248Sbostic 				format |= EFORMAT;
311*34248Sbostic 
312*34248Sbostic 				/* first digit is precision for g/G format */
313*34248Sbostic 				if (prec)
314*34248Sbostic 					--prec;
315*34248Sbostic 			}
316*34248Sbostic 			if (decpt)
317*34248Sbostic 				*p2++ = '.';
318*34248Sbostic 			for (; ++p < endp && prec; --prec, *p2++ = *p);
319*34248Sbostic 
320*34248Sbostic 			/* precision ran out; round number */
321*34248Sbostic 			if (p < endp) {
322*34248Sbostic 				if (*p > '4') {
323*34248Sbostic 					for (savep = p2--;; *p2-- = '0') {
324*34248Sbostic 						if (*p2 == '.')
325*34248Sbostic 							--p2;
326*34248Sbostic 						if (++*p2 <= '9')
327*34248Sbostic 							break;
328*34248Sbostic 					}
329*34248Sbostic 					p2 = savep;
330*34248Sbostic 				}
331*34248Sbostic 				fract = 0;
332*34248Sbostic 			}
33334242Sbostic 		}
334*34248Sbostic 		/*
335*34248Sbostic 		 * g/G in f format; if run out of precision, replace digits
336*34248Sbostic 		 * with zeroes, note, have to round first, otherwise lose
337*34248Sbostic 		 * rounding point.
338*34248Sbostic 		 */
339*34248Sbostic 		else if (format&GFORMAT) {
340*34248Sbostic 			for (; ++p < endp && prec; --prec, *p2++ = *p);
341*34248Sbostic 			/* precision ran out; round and then add zeroes */
342*34248Sbostic 			if (p < endp) {
343*34248Sbostic 				if (*p > '4') {
344*34248Sbostic 					for (savep = p2--; ++*p2 > '9';
345*34248Sbostic 					    *p2-- = '0');
346*34248Sbostic 					p2 = savep;
347*34248Sbostic 				}
348*34248Sbostic 				do {
349*34248Sbostic 					*p2++ = '0';
350*34248Sbostic 				} while (++p < endp);
351*34248Sbostic 				fract = 0;
352*34248Sbostic 			}
353*34248Sbostic 			if (decpt)
354*34248Sbostic 				*p2++ = '.';
35534242Sbostic 		}
356*34248Sbostic 		/* f format */
357*34248Sbostic 		else {
358*34248Sbostic 			for (; ++p < endp; *p2++ = *p);
359*34248Sbostic 			if (decpt)
360*34248Sbostic 				*p2++ = '.';
361*34248Sbostic 		}
362*34248Sbostic 		p = p2;
363*34248Sbostic 	}
364*34248Sbostic 	/*
365*34248Sbostic 	 * it's unclear from the ANSI X3J11 spec if the g/G format should
366*34248Sbostic 	 * just result in an empty string, because it's supposed to remove
367*34248Sbostic 	 * trailing zeroes.  That seems counter-intuitive, so here it does
368*34248Sbostic 	 * what f and e/E do; if no fraction, the number was zero, and if
369*34248Sbostic 	 * no precision can't show anything after the decimal point.
370*34248Sbostic 	 */
371*34248Sbostic 	else if (!fract || !prec) {
372*34248Sbostic 		*startp++ = '0';
373*34248Sbostic 		if (decpt)
374*34248Sbostic 			*startp++ = '.';
375*34248Sbostic 		*startp++ = '\0';
376*34248Sbostic 		return(startp);
377*34248Sbostic 	}
378*34248Sbostic 	/*
379*34248Sbostic 	 * if the format is g/G, and the resulting exponent will be less than
380*34248Sbostic 	 * -4 use e/E format.  If e/E format, compute exponent value.
381*34248Sbostic 	 */
382*34248Sbostic 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
383*34248Sbostic 		format |= EFORMAT;
384*34248Sbostic 		if (fract)
385*34248Sbostic 			for (p = startp; fract;) {
386*34248Sbostic 				fract = modf(fract * 10, &tmp);
387*34248Sbostic 				if (!tmp) {
388*34248Sbostic 					--expcnt;
389*34248Sbostic 					continue;
390*34248Sbostic 				}
391*34248Sbostic 				*p++ = (int)tmp + '0';
392*34248Sbostic 				break;
393*34248Sbostic 			}
39434242Sbostic 		else
395*34248Sbostic 			*p++ = '0';
396*34248Sbostic 
397*34248Sbostic 		/* g/G format, decrement precision for first digit */
398*34248Sbostic 		if (format&GFORMAT && prec)
399*34248Sbostic 			--prec;
400*34248Sbostic 
401*34248Sbostic 		/* add decimal after first non-zero digit */
402*34248Sbostic 		if (decpt)
403*34248Sbostic 			*p++ = '.';
40434242Sbostic 	}
405*34248Sbostic 	/*
406*34248Sbostic 	 * f format or g/G printed as f format; don't worry about decimal
407*34248Sbostic 	 * point, if g/G format doesn't need it, will get stripped later.
408*34248Sbostic 	 */
40934242Sbostic 	else {
410*34248Sbostic 		p = startp;
411*34248Sbostic 		*p++ = '0';
412*34248Sbostic 		*p++ = '.';
413*34248Sbostic 	}
414*34248Sbostic 
415*34248Sbostic 	/* finish out requested precision from fractional value */
416*34248Sbostic 	while (prec--)
417*34248Sbostic 		if (fract) {
418*34248Sbostic 			fract = modf(fract * 10, &tmp);
419*34248Sbostic 			*p++ = (int)tmp + '0';
420*34248Sbostic 		}
421*34248Sbostic 		else
422*34248Sbostic 			*p++ = '0';
423*34248Sbostic 
424*34248Sbostic 	/*
425*34248Sbostic 	 * if any fractional value left, "round" it back up to the beginning
426*34248Sbostic 	 * of the number, fixing the exponent as necessary, and avoiding the
427*34248Sbostic 	 * decimal point.
428*34248Sbostic 	 */
429*34248Sbostic 	if (fract) {
430*34248Sbostic 		(void)modf(fract * 10, &tmp);
431*34248Sbostic 		if (tmp > 4) {
432*34248Sbostic 			for (savep = p--;; *p-- = '0') {
433*34248Sbostic 				if (*p == '.')
434*34248Sbostic 					--p;
435*34248Sbostic 				if (p == startp) {
436*34248Sbostic 					*p = '1';
437*34248Sbostic 					++expcnt;
438*34248Sbostic 					break;
439*34248Sbostic 				}
440*34248Sbostic 				if (++*p <= '9')
441*34248Sbostic 					break;
44234242Sbostic 			}
443*34248Sbostic 			p = savep;
44434242Sbostic 		}
445*34248Sbostic 	}
446*34248Sbostic 
447*34248Sbostic 	/*
448*34248Sbostic 	 * if a g/G format and not alternate flag, lose trailing zeroes,
449*34248Sbostic 	 * if e/E or g/G format, and last char is decimal point, lose it.
450*34248Sbostic 	 */
451*34248Sbostic 	if (!alternate) {
452*34248Sbostic 		if (format&GFORMAT)
453*34248Sbostic 			for (; p[-1] == '0'; --p);
454*34248Sbostic 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
455*34248Sbostic 			--p;
456*34248Sbostic 	}
457*34248Sbostic 
458*34248Sbostic 	/* if an e/E format, add exponent */
459*34248Sbostic 	if (format&EFORMAT) {
460*34248Sbostic 		*p++ = fmtch;
461*34248Sbostic 		if (--expcnt < 0) {
462*34248Sbostic 			expcnt = -expcnt;
463*34248Sbostic 			*p++ = '-';
46434242Sbostic 		}
465*34248Sbostic 		else
466*34248Sbostic 			*p++ = '+';
467*34248Sbostic 		*p++ = expcnt / 10 + '0';
468*34248Sbostic 		*p++ = expcnt % 10 + '0';
46934242Sbostic 	}
470*34248Sbostic 	return(p);
47134242Sbostic }
472