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