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