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