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