xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34235)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 
13 #if defined(LIBC_SCCS) && !defined(lint)
14 static char sccsid[] = "@(#)vfprintf.c	5.3 (Berkeley) 05/08/88";
15 #endif /* LIBC_SCCS and not lint */
16 
17 #include <sys/param.h>
18 #include <varargs.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 
22 #define	GETARG(r) \
23 	r = argsize&LONGINT ? va_arg(argp, long) : \
24 	    argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
25 
26 #define	DEFPREC		6			/* default precision */
27 #define	MAXBUF		1024
28 #define	PUTC(ch, fd)	{ ++cnt; putc(ch, fd); }
29 #define	todigit(ch)	((ch) - '0')
30 #define	tochar(ch)	((ch) + '0')
31 
32 #define	LONGINT		0x01
33 #define	LONGDBL		0x02
34 #define	SHORTINT	0x04
35 
36 x_doprnt(fmt, argp, fp)
37 	register char *fmt;
38 	va_list argp;
39 	register FILE *fp;
40 {
41 	register u_long reg_ulong;
42 	register long reg_long;
43 	register int base;
44 	register char *digs, *bp, *t, padc;
45 	double _double;
46 	char argsize, printsign, buf[MAXBUF], *fcvt();
47 	int alternate, cnt, decpt, n, ladjust, width, prec, sign, size;
48 
49 	for (cnt = 0; *fmt; ++fmt) {
50 		if (*fmt != '%') {
51 			PUTC(*fmt, fp);
52 			continue;
53 		}
54 
55 		alternate = ladjust = width = 0;
56 		prec = -1;
57 		padc = ' ';
58 		argsize = printsign = '\0';
59 
60 flags:		switch (*++fmt) {
61 		case '#':
62 			alternate = 1;
63 			goto flags;
64 		case '%':			/* "%#%" prints as "%" */
65 			PUTC('%', fp);
66 			continue;
67 		case '*':
68 			/*
69 			 * ``A negative field width argument is taken as a
70 			 * - flag followed by a  positive field width.''
71 			 *	-- ANSI X3J11
72 			 * They don't exclude field widths read from args.
73 			 */
74 			if ((width = va_arg(argp, int)) >= 0)
75 				goto flags;
76 			width = -width;
77 			/*FALLTHROUGH*/
78 		case '-':
79 			ladjust = 1;
80 			goto flags;
81 		case '+':
82 			printsign = '+';
83 			goto flags;
84 		case '.':
85 			if (*++fmt == '*')
86 				prec = va_arg(argp, int);
87 			else if (isdigit(*fmt)) {
88 				prec = 0;
89 				do {
90 					prec = 10 * prec + todigit(*fmt);
91 				} while isdigit(*++fmt);
92 				--fmt;
93 			}
94 			else {
95 				prec = 0;
96 				--fmt;
97 				goto flags;
98 			}
99 			if (prec < 0)
100 				prec = -1;
101 			goto flags;
102 		case '0':
103 			padc = '0';
104 			/*FALLTHROUGH*/
105 		case '1': case '2': case '3': case '4':
106 		case '5': case '6': case '7': case '8': case '9':
107 			do {
108 				width = 10 * width + todigit(*fmt);
109 			} while isdigit(*++fmt);
110 			--fmt;
111 		case 'L':
112 			argsize |= LONGDBL;
113 			goto flags;
114 		case 'h':
115 			argsize |= SHORTINT;
116 			goto flags;
117 		case 'l':
118 			argsize |= LONGINT;
119 			goto flags;
120 		}
121 
122 		digs = "0123456789abcdef";
123 
124 		switch (*fmt) {
125 		case 'c':
126 			PUTC(va_arg(argp, int), fp);
127 			break;
128 		case 'f':
129 			if (prec == -1)
130 				prec = DEFPREC;
131 			_double = va_arg(argp, double);
132 			t = fcvt(_double, prec + 1, &decpt, &sign);
133 			if (sign)
134 				printsign = '-';
135 			bp = buf;
136 			if (decpt >= 0)
137 				for (;;) {
138 					*bp++ = *t ? *t++ : '0';
139 					if (!--decpt)
140 						break;
141 				}
142 			if (alternate || prec > 0)
143 				*bp++ = '.';
144 			while (decpt++) {
145 				*bp++ = *t ? *t++ : '0';
146 				--prec;
147 			}
148 			while (prec--)
149 				*bp++ = *t ? *t++ : '0';
150 			size = bp - buf;
151 			if (size < width && !ladjust)
152 				do {
153 					PUTC(padc, fp);
154 				} while (--width > size);
155 			for (t = buf; t < bp; ++t)
156 				PUTC(*t, fp);
157 			for (; width > size; --width)
158 				PUTC(padc, fp);
159 			break;
160 		case 'd':
161 		case 'i':
162 			GETARG(reg_long);
163 			if (reg_long < 0) {
164 				reg_ulong = -reg_long;
165 				printsign = '-';
166 			}
167 			else {
168 				reg_ulong = reg_long;
169 			}
170 			if (printsign)
171 				PUTC(printsign, fp);
172 			base = 10;
173 			goto num1;
174 		case 'n':
175 			*(va_arg(argp, int *)) = cnt;
176 			break;
177 		case 'o':
178 			GETARG(reg_ulong);
179 			base = 8;
180 			if (!reg_ulong || !alternate)
181 				goto num1;
182 			bp = buf + sizeof(buf) - 1;
183 			do {
184 				*bp-- = digs[reg_ulong % base];
185 				reg_ulong /= base;
186 			} while(reg_ulong);
187 			size = &buf[sizeof(buf) - 1] - bp;
188 			if (size < --width && !ladjust)
189 				do {
190 					PUTC(padc, fp);
191 				} while (--width > size);
192 			PUTC('0', fp);
193 			goto num3;
194 			break;
195 		case 'p':
196 		case 's':
197 			if (!(bp = va_arg(argp, char *)))
198 				bp = "(null)";
199 			if (width > 0 && !ladjust) {
200 				char *savep;
201 
202 				savep = bp;
203 				for (n = 0; *bp && (prec < 0 || n < prec);
204 				    n++, bp++);
205 				bp = savep;
206 				while (n++ < width)
207 					PUTC(' ', fp);
208 			}
209 			for (n = 0; *bp; ++bp) {
210 				if (++n > prec && prec >= 0)
211 					break;
212 				PUTC(*bp, fp);
213 			}
214 			if (n < width && ladjust)
215 				do {
216 					PUTC(' ', fp);
217 				} while (++n < width);
218 			break;
219 		case 'u':
220 			GETARG(reg_ulong);
221 			base = 10;
222 			goto num1;
223 		case 'X':
224 			digs = "0123456789ABCDEF";
225 			/*FALLTHROUGH*/
226 		case 'x':
227 			GETARG(reg_ulong);
228 			if (alternate && reg_ulong) {
229 				PUTC('0', fp);
230 				PUTC(*fmt, fp);
231 			}
232 			base = 16;
233 num1:			bp = buf + sizeof(buf) - 1;
234 			do {
235 				*bp-- = digs[reg_ulong % base];
236 				reg_ulong /= base;
237 			} while(reg_ulong);
238 			size = &buf[sizeof(buf) - 1] - bp;
239 			for (; size < prec; *bp-- = '0', ++size);
240 			if (size < width && !ladjust)
241 				do {
242 					PUTC(padc, fp);
243 				} while (--width > size);
244 num3:			while (++bp != &buf[MAXBUF])
245 				PUTC(*bp, fp);
246 			for (; width > size; --width)
247 				PUTC(padc, fp);
248 			break;
249 		case '\0':		/* "%?" prints ?, unless ? is NULL */
250 			return(ferror(fp) ? -1 : cnt);
251 		default:
252 			PUTC(*fmt, fp);
253 		}
254 	}
255 	return(ferror(fp) ? -1 : cnt);
256 }
257 
258