xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34314)
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.11 (Berkeley) 05/16/88";
15 #endif /* LIBC_SCCS and not lint */
16 
17 #include <sys/types.h>
18 #include <varargs.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 
22 #define	MAXBUF	40
23 
24 #define	PUTC(ch)	{++cnt; putc(ch, fp);}
25 
26 #define	LONGINT		0x01
27 #define	LONGDBL		0x02
28 #define	SHORTINT	0x04
29 #define	GETARG(r) \
30 	r = argsize&LONGINT ? va_arg(argp, long) : \
31 	    argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
32 
33 static int alt;
34 static char sign;
35 
36 x_doprnt(fmt, argp, fp)
37 	register char *fmt;
38 	va_list argp;
39 	register FILE *fp;
40 {
41 	register int cnt, n;
42 	register char ch, *t;
43 	double _double;
44 	u_long _ulong;
45 	int base, ladjust, width, prec, size;
46 	char argsize, padc, *digs, *_cvt(), buf[MAXBUF];
47 
48 	digs = "0123456789abcdef";
49 	for (cnt = 0;; ++fmt) {
50 		if ((n = fp->_cnt) >= 0) {
51 			for (t = fp->_ptr; (ch = *fmt) != '%' && ch; ++fmt) {
52 				if (--n < 0)
53 					break;
54 				*t++ = ch;
55 			}
56 			fp->_ptr = t;
57 			cnt += fp->_cnt - n;
58 			fp->_cnt = n;
59 			if (ch != '%' && ch) {
60 				PUTC(ch);
61 				continue;
62 			}
63 		}
64 		else for (; *fmt && *fmt != '%'; ++fmt)
65 			PUTC(*fmt);
66 
67 		if (!*fmt)
68 			return(cnt);
69 
70 		alt = ladjust = width = 0;
71 		prec = -1;
72 		padc = ' ';
73 		argsize = sign = '\0';
74 		t = buf;
75 
76 flags:		switch (*++fmt) {
77 		case '#':
78 			alt = 1;
79 			goto flags;
80 		case '*':
81 			/*
82 			 * ``A negative field width argument is taken as a
83 			 * - flag followed by a  positive field width.''
84 			 *	-- ANSI X3J11
85 			 * They don't exclude field widths read from args.
86 			 */
87 			if ((width = va_arg(argp, int)) >= 0)
88 				goto flags;
89 			width = -width;
90 			/*FALLTHROUGH*/
91 		case '-':
92 			ladjust = 1;
93 			goto flags;
94 		case '+':
95 			sign = '+';
96 			goto flags;
97 		case '.':
98 			if (*++fmt == '*')
99 				prec = va_arg(argp, int);
100 			else if (isdigit(*fmt)) {
101 				prec = 0;
102 				do {
103 					prec = 10 * prec + *fmt - '0';
104 				} while isdigit(*++fmt);
105 				--fmt;
106 			}
107 			else {
108 				prec = 0;
109 				--fmt;
110 				goto flags;
111 			}
112 			if (prec < 0)
113 				prec = -1;
114 			goto flags;
115 		case '0':
116 			padc = '0';
117 			/*FALLTHROUGH*/
118 		case '1': case '2': case '3': case '4':
119 		case '5': case '6': case '7': case '8': case '9':
120 			do {
121 				width = 10 * width + *fmt - '0';
122 			} while isdigit(*++fmt);
123 			--fmt;
124 		case 'L':
125 			argsize |= LONGDBL;
126 			goto flags;
127 		case 'h':
128 			argsize |= SHORTINT;
129 			goto flags;
130 		case 'l':
131 			argsize |= LONGINT;
132 			goto flags;
133 		case 'c':
134 			*t = va_arg(argp, int);
135 			size = 1;
136 			goto pforw;
137 		case 'd':
138 		case 'i': {
139 			long reg_long;
140 
141 			GETARG(reg_long);
142 			if (reg_long < 0) {
143 				_ulong = -reg_long;
144 				sign = '-';
145 			}
146 			else {
147 				_ulong = reg_long;
148 			}
149 			if (sign)
150 				PUTC(sign);
151 			base = 10;
152 			goto num;
153 		}
154 		case 'e':
155 		case 'E':
156 		case 'f':
157 		case 'g':
158 		case 'G':
159 			_double = va_arg(argp, double);
160 			size = _cvt(_double, prec, buf, buf + sizeof(buf),
161 			    *fmt) - buf;
162 			goto pforw;
163 		case 'n':
164 			*(va_arg(argp, int *)) = cnt;
165 			break;
166 		case 'o':
167 			GETARG(_ulong);
168 			base = 8;
169 			goto num;
170 		case 'p':
171 		case 's':
172 			if (!(t = va_arg(argp, char *)))
173 				t = "(null)";
174 			if ((size = strlen(t)) < prec)
175 				size = prec;
176 pforw:			if (!ladjust && width)
177 				for (n = size; n++ < width;)
178 					PUTC(padc);
179 			if (fp->_cnt - (n = size) >= 0) {
180 				cnt += n;
181 				fp->_cnt -= n;
182 				bcopy(t, fp->_ptr, n);
183 				fp->_ptr += n;
184 			}
185 			else for (; n--; ++t)
186 				PUTC(*t);
187 			if (ladjust)
188 				while (width-- > size)
189 					PUTC(padc);
190 			break;
191 		case 'u':
192 			GETARG(_ulong);
193 			base = 10;
194 			goto num;
195 		case 'X':
196 			digs = "0123456789ABCDEF";
197 			/*FALLTHROUGH*/
198 		case 'x':
199 			GETARG(_ulong);
200 			base = 16;
201 			/* alternate form for hex; leading 0x/X */
202 			if (alt && _ulong) {
203 				PUTC('0');
204 				PUTC(*fmt);
205 			}
206 num:			t = buf + sizeof(buf) - 1;
207 			do {
208 				*t-- = digs[_ulong % base];
209 				_ulong /= base;
210 			} while(_ulong);
211 			digs = "0123456789abcdef";
212 			size = buf + sizeof(buf) - 1 - t;
213 			if (size >= prec) {
214 				/* alternate form for octal; leading 0 */
215 				if (t[1] != '0' && alt && *fmt == 'o') {
216 					*t-- = '0';
217 					++size;
218 				}
219 			}
220 			else
221 				for (; size < prec; ++size)
222 					*t-- = '0';
223 			if (!ladjust)
224 				while (size++ < width)
225 					PUTC(padc);
226 			while (++t < buf + sizeof(buf))
227 				PUTC(*t);
228 			for (; width > size; --width)
229 				PUTC(padc);
230 			break;
231 		case '\0':		/* "%?" prints ?, unless ? is NULL */
232 			return(cnt);
233 		default:
234 			PUTC(*fmt);
235 		}
236 	}
237 	/*NOTREACHED*/
238 }
239 
240 #define	EFORMAT	0x01
241 #define	FFORMAT	0x02
242 #define	GFORMAT	0x04
243 #define	DEFPREC	6
244 
245 static char *
246 _cvt(number, prec, startp, endp, fmtch)
247 	double number;
248 	register int prec;
249 	char *startp, *endp, fmtch;
250 {
251 	register char *p;
252 	register int expcnt, format;
253 	double fract, integer, tmp, modf();
254 	int decpt;
255 	char *savep;
256 
257 	if (prec == -1)
258 		prec = DEFPREC;
259 
260 	if (number < 0) {
261 		*startp++ = '-';
262 		number = -number;
263 	}
264 	else if (sign)
265 		*startp++ = '+';
266 
267 	switch(fmtch) {
268 	case 'e':
269 	case 'E':
270 		format = EFORMAT;
271 		break;
272 	case 'f':
273 		format = FFORMAT;
274 		break;
275 	case 'g':
276 	case 'G':
277 		format = GFORMAT;
278 		fmtch -= 2;
279 	}
280 
281 	/*
282 	 * if the alternate flag is set, or, at least one digit of precision
283 	 * was requested, add a decimal point, unless it's the g/G format
284 	 * in which case we require two digits of precision, as it counts
285 	 * precision differently.
286 	 */
287 	decpt = alt || prec > 1 || !(format&GFORMAT) && prec;
288 
289 	expcnt = 0;
290 	p = endp - 1;
291 	fract = modf(number, &integer);
292 	if (integer) {
293 		register char *p2;
294 
295 		/* get integer part of number; count decimal places */
296 		for (; integer; ++expcnt) {
297 			tmp = modf(integer / 10, &integer);
298 			*p-- = (int)((tmp + .03) * 10) + '0';
299 		}
300 
301 		/* copy, in reverse order, to start of buffer */
302 		p2 = startp;
303 		*p2++ = *++p;
304 
305 		/*
306 		 * if the format is g/G, and the resulting exponent will be
307 		 * greater than the precision, use e/E format.  If e/E format,
308 		 * put in a decimal point as needed, and decrement precision
309 		 * count for each digit after the decimal point.
310 		 */
311 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
312 			if (format&GFORMAT) {
313 				format |= EFORMAT;
314 
315 				/* first digit is precision for g/G format */
316 				if (prec)
317 					--prec;
318 			}
319 			if (decpt)
320 				*p2++ = '.';
321 			for (; ++p < endp && prec; --prec, *p2++ = *p);
322 
323 			/* precision ran out; round number */
324 			if (p < endp) {
325 				if (*p > '4') {
326 					for (savep = p2--;; *p2-- = '0') {
327 						if (*p2 == '.')
328 							--p2;
329 						if (++*p2 <= '9')
330 							break;
331 					}
332 					p2 = savep;
333 				}
334 				fract = 0;
335 			}
336 		}
337 		/*
338 		 * g/G in f format; if out of precision, replace digits with
339 		 * zeroes, note, have to round first.
340 		 */
341 		else if (format&GFORMAT) {
342 			for (; ++p < endp && prec; --prec, *p2++ = *p);
343 			/* precision ran out; round and then add zeroes */
344 			if (p < endp) {
345 				if (*p > '4') {
346 					for (savep = p2--; ++*p2 > '9';
347 					    *p2-- = '0');
348 					p2 = savep;
349 				}
350 				do {
351 					*p2++ = '0';
352 				} while (++p < endp);
353 				fract = 0;
354 			}
355 			if (decpt)
356 				*p2++ = '.';
357 		}
358 		/* f format */
359 		else {
360 			for (; ++p < endp; *p2++ = *p);
361 			if (decpt)
362 				*p2++ = '.';
363 		}
364 		p = p2;
365 	}
366 	/*
367 	 * it's unclear from the ANSI X3J11 spec if the g/G format should
368 	 * just result in an empty string, because it's supposed to remove
369 	 * trailing zeroes.  That seems counter-intuitive, so here it does
370 	 * what f and e/E do; if no fraction, the number was zero, and if
371 	 * no precision, can't show anything after the decimal point.
372 	 */
373 	else if (!fract || !prec) {
374 		*startp++ = '0';
375 		if (decpt)
376 			*startp++ = '.';
377 		*startp++ = '\0';
378 		return(startp);
379 	}
380 	/*
381 	 * if the format is g/G, and the resulting exponent will be less than
382 	 * -4 use e/E format.  If e/E format, compute exponent value.
383 	 */
384 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
385 		format |= EFORMAT;
386 		if (fract)
387 			for (p = startp; fract;) {
388 				fract = modf(fract * 10, &tmp);
389 				if (!tmp) {
390 					--expcnt;
391 					continue;
392 				}
393 				*p++ = (int)tmp + '0';
394 				break;
395 			}
396 		else
397 			*p++ = '0';
398 
399 		/* g/G format, decrement precision for first digit */
400 		if (format&GFORMAT && prec)
401 			--prec;
402 
403 		/* add decimal after first non-zero digit */
404 		if (decpt)
405 			*p++ = '.';
406 	}
407 	/*
408 	 * f format or g/G printed as f format; don't worry about decimal
409 	 * point, if g/G format doesn't need it, will get stripped later.
410 	 */
411 	else {
412 		p = startp;
413 		*p++ = '0';
414 		*p++ = '.';
415 	}
416 
417 	/* finish out requested precision from fractional value */
418 	while (prec--)
419 		if (fract) {
420 			fract = modf(fract * 10, &tmp);
421 			*p++ = (int)tmp + '0';
422 		}
423 		else
424 			*p++ = '0';
425 
426 	/*
427 	 * if any fractional value left, "round" it back up to the beginning
428 	 * of the number, fixing the exponent as necessary, and avoiding the
429 	 * decimal point.
430 	 */
431 	if (fract) {
432 		(void)modf(fract * 10, &tmp);
433 		if (tmp > 4) {
434 			for (savep = p--;; *p-- = '0') {
435 				if (*p == '.')
436 					--p;
437 				if (p == startp) {
438 					*p = '1';
439 					++expcnt;
440 					break;
441 				}
442 				if (++*p <= '9')
443 					break;
444 			}
445 			p = savep;
446 		}
447 	}
448 
449 	/*
450 	 * if a g/G format and not alternate flag, lose trailing zeroes,
451 	 * if e/E or g/G format, and last char is decimal point, lose it.
452 	 */
453 	if (!alt) {
454 		if (format&GFORMAT)
455 			for (; p[-1] == '0'; --p);
456 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
457 			--p;
458 	}
459 
460 	/* if an e/E format, add exponent */
461 	if (format&EFORMAT) {
462 		*p++ = fmtch;
463 		if (--expcnt < 0) {
464 			expcnt = -expcnt;
465 			*p++ = '-';
466 		}
467 		else
468 			*p++ = '+';
469 		*p++ = expcnt / 10 + '0';
470 		*p++ = expcnt % 10 + '0';
471 	}
472 	return(p);
473 }
474