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