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