xref: /openbsd-src/lib/libc/stdio/vfprintf.c (revision ce7e0fc6a9d74d25b78fb6ad846387717f5172b6)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Chris Torek.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #if defined(LIBC_SCCS) && !defined(lint)
38 static char *rcsid = "$OpenBSD: vfprintf.c,v 1.17 2002/02/19 19:39:37 millert Exp $";
39 #endif /* LIBC_SCCS and not lint */
40 
41 /*
42  * Actual printf innards.
43  *
44  * This code is large and complicated...
45  */
46 
47 #include <sys/types.h>
48 
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <errno.h>
53 #include <stdarg.h>
54 
55 #include "local.h"
56 #include "fvwrite.h"
57 
58 static void __find_arguments(const char *fmt0, va_list ap, va_list **argtable);
59 static int __grow_type_table(unsigned char **typetable, int *tablesize);
60 
61 /*
62  * Flush out all the vectors defined by the given uio,
63  * then reset it so that it can be reused.
64  */
65 static int
66 __sprint(fp, uio)
67 	FILE *fp;
68 	register struct __suio *uio;
69 {
70 	register int err;
71 
72 	if (uio->uio_resid == 0) {
73 		uio->uio_iovcnt = 0;
74 		return (0);
75 	}
76 	err = __sfvwrite(fp, uio);
77 	uio->uio_resid = 0;
78 	uio->uio_iovcnt = 0;
79 	return (err);
80 }
81 
82 /*
83  * Helper function for `fprintf to unbuffered unix file': creates a
84  * temporary buffer.  We only work on write-only files; this avoids
85  * worries about ungetc buffers and so forth.
86  */
87 static int
88 __sbprintf(fp, fmt, ap)
89 	register FILE *fp;
90 	const char *fmt;
91 	va_list ap;
92 {
93 	int ret;
94 	FILE fake;
95 	unsigned char buf[BUFSIZ];
96 
97 	/* copy the important variables */
98 	fake._flags = fp->_flags & ~__SNBF;
99 	fake._file = fp->_file;
100 	fake._cookie = fp->_cookie;
101 	fake._write = fp->_write;
102 
103 	/* set up the buffer */
104 	fake._bf._base = fake._p = buf;
105 	fake._bf._size = fake._w = sizeof(buf);
106 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
107 
108 	/* do the work, then copy any error status */
109 	ret = vfprintf(&fake, fmt, ap);
110 	if (ret >= 0 && fflush(&fake))
111 		ret = EOF;
112 	if (fake._flags & __SERR)
113 		fp->_flags |= __SERR;
114 	return (ret);
115 }
116 
117 
118 #ifdef FLOATING_POINT
119 #include <locale.h>
120 #include <math.h>
121 #include "floatio.h"
122 
123 #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
124 #define	DEFPREC		6
125 
126 static char *cvt(double, int, int, char *, int *, int, int *);
127 static int exponent(char *, int, int);
128 
129 #else /* no FLOATING_POINT */
130 #define	BUF		40
131 #endif /* FLOATING_POINT */
132 
133 #define STATIC_ARG_TBL_SIZE 8	/* Size of static argument table. */
134 
135 
136 /*
137  * Macros for converting digits to letters and vice versa
138  */
139 #define	to_digit(c)	((c) - '0')
140 #define is_digit(c)	((unsigned)to_digit(c) <= 9)
141 #define	to_char(n)	((n) + '0')
142 
143 /*
144  * Flags used during conversion.
145  */
146 #define	ALT		0x001		/* alternate form */
147 #define	HEXPREFIX	0x002		/* add 0x or 0X prefix */
148 #define	LADJUST		0x004		/* left adjustment */
149 #define	LONGDBL		0x008		/* long double; unimplemented */
150 #define	LONGINT		0x010		/* long integer */
151 #define	QUADINT		0x020		/* quad integer */
152 #define	SHORTINT	0x040		/* short integer */
153 #define	ZEROPAD		0x080		/* zero (as opposed to blank) pad */
154 #define FPT		0x100		/* Floating point number */
155 int
156 vfprintf(fp, fmt0, ap)
157 	FILE *fp;
158 	const char *fmt0;
159 	_BSD_VA_LIST_ ap;
160 {
161 	register char *fmt;	/* format string */
162 	register int ch;	/* character from fmt */
163 	register int n, m, n2;	/* handy integers (short term usage) */
164 	register char *cp;	/* handy char pointer (short term usage) */
165 	register struct __siov *iovp;/* for PRINT macro */
166 	register int flags;	/* flags as above */
167 	int ret;		/* return value accumulator */
168 	int width;		/* width from format (%8d), or 0 */
169 	int prec;		/* precision from format (%.3d), or -1 */
170 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
171 	wchar_t wc;
172 #ifdef FLOATING_POINT
173 	char *decimal_point = localeconv()->decimal_point;
174 	char softsign;		/* temporary negative sign for floats */
175 	double _double;		/* double precision arguments %[eEfgG] */
176 	int expt;		/* integer value of exponent */
177 	int expsize;		/* character count for expstr */
178 	int ndig;		/* actual number of digits returned by cvt */
179 	char expstr[7];		/* buffer for exponent string */
180 #endif
181 
182 #ifdef __GNUC__			/* gcc has builtin quad type (long long) SOS */
183 #define	quad_t	  long long
184 #define	u_quad_t  unsigned long long
185 #endif
186 
187 	u_quad_t _uquad;	/* integer arguments %[diouxX] */
188 	enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
189 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
190 	int realsz;		/* field size expanded by dprec */
191 	int size;		/* size of converted field or string */
192 	char *xdigs;		/* digits for [xX] conversion */
193 #define NIOV 8
194 	struct __suio uio;	/* output information: summary */
195 	struct __siov iov[NIOV];/* ... and individual io vectors */
196 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
197 	char ox[2];		/* space for 0x hex-prefix */
198 	va_list *argtable;	/* args, built due to positional arg */
199 	va_list statargtable[STATIC_ARG_TBL_SIZE];
200 	int nextarg;		/* 1-based argument index */
201 	va_list orgap;		/* original argument pointer */
202 
203 	/*
204 	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
205 	 * fields occur frequently, increase PADSIZE and make the initialisers
206 	 * below longer.
207 	 */
208 #define	PADSIZE	16		/* pad chunk size */
209 	static char blanks[PADSIZE] =
210 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
211 	static char zeroes[PADSIZE] =
212 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
213 
214 	/*
215 	 * BEWARE, these `goto error' on error, and PAD uses `n'.
216 	 */
217 #define	PRINT(ptr, len) do { \
218 	iovp->iov_base = (ptr); \
219 	iovp->iov_len = (len); \
220 	uio.uio_resid += (len); \
221 	iovp++; \
222 	if (++uio.uio_iovcnt >= NIOV) { \
223 		if (__sprint(fp, &uio)) \
224 			goto error; \
225 		iovp = iov; \
226 	} \
227 } while (0)
228 #define	PAD(howmany, with) do { \
229 	if ((n = (howmany)) > 0) { \
230 		while (n > PADSIZE) { \
231 			PRINT(with, PADSIZE); \
232 			n -= PADSIZE; \
233 		} \
234 		PRINT(with, n); \
235 	} \
236 } while (0)
237 #define	FLUSH() do { \
238 	if (uio.uio_resid && __sprint(fp, &uio)) \
239 		goto error; \
240 	uio.uio_iovcnt = 0; \
241 	iovp = iov; \
242 } while (0)
243 
244 	/*
245 	 * To extend shorts properly, we need both signed and unsigned
246 	 * argument extraction methods.
247 	 */
248 #define	SARG() \
249 	(flags&QUADINT ? va_arg(ap, quad_t) : \
250 	    flags&LONGINT ? GETARG(long) : \
251 	    flags&SHORTINT ? (long)(short)GETARG(int) : \
252 	    (long)GETARG(int))
253 #define	UARG() \
254 	(flags&QUADINT ? va_arg(ap, u_quad_t) : \
255 	    flags&LONGINT ? GETARG(u_long) : \
256 	    flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
257 	    (u_long)GETARG(u_int))
258 
259 	 /*
260 	  * Get * arguments, including the form *nn$.  Preserve the nextarg
261 	  * that the argument can be gotten once the type is determined.
262 	  */
263 #define GETASTER(val) \
264 	n2 = 0; \
265 	cp = fmt; \
266 	while (is_digit(*cp)) { \
267 		n2 = 10 * n2 + to_digit(*cp); \
268 		cp++; \
269 	} \
270 	if (*cp == '$') { \
271 		int hold = nextarg; \
272 		if (argtable == NULL) { \
273 			argtable = statargtable; \
274 			__find_arguments(fmt0, orgap, &argtable); \
275 		} \
276 		nextarg = n2; \
277 		val = GETARG(int); \
278 		nextarg = hold; \
279 		fmt = ++cp; \
280 	} else { \
281 		val = GETARG(int); \
282 	}
283 
284 /*
285 * Get the argument indexed by nextarg.   If the argument table is
286 * built, use it to get the argument.  If its not, get the next
287 * argument (and arguments must be gotten sequentially).
288 */
289 #define GETARG(type) \
290 	(((argtable != NULL) ? (void)(ap = argtable[nextarg]) : (void)0), \
291 	 nextarg++, va_arg(ap, type))
292 
293 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
294 	if (cantwrite(fp)) {
295 		errno = EBADF;
296 		return (EOF);
297 	}
298 
299 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
300 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
301 	    fp->_file >= 0)
302 		return (__sbprintf(fp, fmt0, ap));
303 
304 	fmt = (char *)fmt0;
305 	argtable = NULL;
306 	nextarg = 1;
307 	orgap = ap;
308 	uio.uio_iov = iovp = iov;
309 	uio.uio_resid = 0;
310 	uio.uio_iovcnt = 0;
311 	ret = 0;
312 
313 	/*
314 	 * Scan the format for conversions (`%' character).
315 	 */
316 	for (;;) {
317 		cp = fmt;
318 		while ((n = mbtowc(&wc, fmt, MB_CUR_MAX)) > 0) {
319 			fmt += n;
320 			if (wc == '%') {
321 				fmt--;
322 				break;
323 			}
324 		}
325 		if ((m = fmt - cp) != 0) {
326 			PRINT(cp, m);
327 			ret += m;
328 		}
329 		if (n <= 0)
330 			goto done;
331 		fmt++;		/* skip over '%' */
332 
333 		flags = 0;
334 		dprec = 0;
335 		width = 0;
336 		prec = -1;
337 		sign = '\0';
338 
339 rflag:		ch = *fmt++;
340 reswitch:	switch (ch) {
341 		case ' ':
342 			/*
343 			 * ``If the space and + flags both appear, the space
344 			 * flag will be ignored.''
345 			 *	-- ANSI X3J11
346 			 */
347 			if (!sign)
348 				sign = ' ';
349 			goto rflag;
350 		case '#':
351 			flags |= ALT;
352 			goto rflag;
353 		case '*':
354 			/*
355 			 * ``A negative field width argument is taken as a
356 			 * - flag followed by a positive field width.''
357 			 *	-- ANSI X3J11
358 			 * They don't exclude field widths read from args.
359 			 */
360 			GETASTER(width);
361 			if (width >= 0)
362 				goto rflag;
363 			width = -width;
364 			/* FALLTHROUGH */
365 		case '-':
366 			flags |= LADJUST;
367 			goto rflag;
368 		case '+':
369 			sign = '+';
370 			goto rflag;
371 		case '.':
372 			if ((ch = *fmt++) == '*') {
373 				GETASTER(n);
374 				prec = n < 0 ? -1 : n;
375 				goto rflag;
376 			}
377 			n = 0;
378 			while (is_digit(ch)) {
379 				n = 10 * n + to_digit(ch);
380 				ch = *fmt++;
381 			}
382 			if (ch == '$') {
383 				nextarg = n;
384 				if (argtable == NULL) {
385 					argtable = statargtable;
386 					__find_arguments(fmt0, orgap,
387 					    &argtable);
388 				}
389 				goto rflag;
390 			}
391 			prec = n < 0 ? -1 : n;
392 			goto reswitch;
393 		case '0':
394 			/*
395 			 * ``Note that 0 is taken as a flag, not as the
396 			 * beginning of a field width.''
397 			 *	-- ANSI X3J11
398 			 */
399 			flags |= ZEROPAD;
400 			goto rflag;
401 		case '1': case '2': case '3': case '4':
402 		case '5': case '6': case '7': case '8': case '9':
403 			n = 0;
404 			do {
405 				n = 10 * n + to_digit(ch);
406 				ch = *fmt++;
407 			} while (is_digit(ch));
408 			if (ch == '$') {
409 				nextarg = n;
410 				if (argtable == NULL) {
411 					argtable = statargtable;
412 					__find_arguments(fmt0, orgap,
413 					    &argtable);
414 				}
415 				goto rflag;
416 			}
417 			width = n;
418 			goto reswitch;
419 #ifdef FLOATING_POINT
420 		case 'L':
421 			flags |= LONGDBL;
422 			goto rflag;
423 #endif
424 		case 'h':
425 			flags |= SHORTINT;
426 			goto rflag;
427 		case 'l':
428 			if (*fmt == 'l') {
429 				fmt++;
430 				flags |= QUADINT;
431 			} else {
432 				flags |= LONGINT;
433 			}
434 			goto rflag;
435 		case 'q':
436 			flags |= QUADINT;
437 			goto rflag;
438 		case 'c':
439 			*(cp = buf) = GETARG(int);
440 			size = 1;
441 			sign = '\0';
442 			break;
443 		case 'D':
444 			flags |= LONGINT;
445 			/*FALLTHROUGH*/
446 		case 'd':
447 		case 'i':
448 			_uquad = SARG();
449 			if ((quad_t)_uquad < 0) {
450 				_uquad = -_uquad;
451 				sign = '-';
452 			}
453 			base = DEC;
454 			goto number;
455 #ifdef FLOATING_POINT
456 		case 'e':
457 		case 'E':
458 		case 'f':
459 		case 'g':
460 		case 'G':
461 			if (prec == -1) {
462 				prec = DEFPREC;
463 			} else if ((ch == 'g' || ch == 'G') && prec == 0) {
464 				prec = 1;
465 			}
466 
467 			if (flags & LONGDBL) {
468 				_double = (double) GETARG(long double);
469 			} else {
470 				_double = GETARG(double);
471 			}
472 
473 			/* do this before tricky precision changes */
474 			if (isinf(_double)) {
475 				if (_double < 0)
476 					sign = '-';
477 				cp = "Inf";
478 				size = 3;
479 				break;
480 			}
481 			if (isnan(_double)) {
482 				cp = "NaN";
483 				size = 3;
484 				break;
485 			}
486 
487 			flags |= FPT;
488 			cp = cvt(_double, prec, flags, &softsign,
489 				&expt, ch, &ndig);
490 			if (ch == 'g' || ch == 'G') {
491 				if (expt <= -4 || expt > prec)
492 					ch = (ch == 'g') ? 'e' : 'E';
493 				else
494 					ch = 'g';
495 			}
496 			if (ch <= 'e') {	/* 'e' or 'E' fmt */
497 				--expt;
498 				expsize = exponent(expstr, expt, ch);
499 				size = expsize + ndig;
500 				if (ndig > 1 || flags & ALT)
501 					++size;
502 			} else if (ch == 'f') {		/* f fmt */
503 				if (expt > 0) {
504 					size = expt;
505 					if (prec || flags & ALT)
506 						size += prec + 1;
507 				} else	/* "0.X" */
508 					size = prec + 2;
509 			} else if (expt >= ndig) {	/* fixed g fmt */
510 				size = expt;
511 				if (flags & ALT)
512 					++size;
513 			} else
514 				size = ndig + (expt > 0 ?
515 					1 : 2 - expt);
516 
517 			if (softsign)
518 				sign = '-';
519 			break;
520 #endif /* FLOATING_POINT */
521 		case 'n':
522 			if (flags & QUADINT)
523 				*GETARG(quad_t *) = ret;
524 			else if (flags & LONGINT)
525 				*GETARG(long *) = ret;
526 			else if (flags & SHORTINT)
527 				*GETARG(short *) = ret;
528 			else
529 				*GETARG(int *) = ret;
530 			continue;	/* no output */
531 		case 'O':
532 			flags |= LONGINT;
533 			/*FALLTHROUGH*/
534 		case 'o':
535 			_uquad = UARG();
536 			base = OCT;
537 			goto nosign;
538 		case 'p':
539 			/*
540 			 * ``The argument shall be a pointer to void.  The
541 			 * value of the pointer is converted to a sequence
542 			 * of printable characters, in an implementation-
543 			 * defined manner.''
544 			 *	-- ANSI X3J11
545 			 */
546 			/* NOSTRICT */
547 			_uquad = (u_long)GETARG(void *);
548 			base = HEX;
549 			xdigs = "0123456789abcdef";
550 			flags |= HEXPREFIX;
551 			ch = 'x';
552 			goto nosign;
553 		case 's':
554 			if ((cp = GETARG(char *)) == NULL)
555 				cp = "(null)";
556 			if (prec >= 0) {
557 				/*
558 				 * can't use strlen; can only look for the
559 				 * NUL in the first `prec' characters, and
560 				 * strlen() will go further.
561 				 */
562 				char *p = memchr(cp, 0, prec);
563 
564 				if (p != NULL) {
565 					size = p - cp;
566 					if (size > prec)
567 						size = prec;
568 				} else
569 					size = prec;
570 			} else
571 				size = strlen(cp);
572 			sign = '\0';
573 			break;
574 		case 'U':
575 			flags |= LONGINT;
576 			/*FALLTHROUGH*/
577 		case 'u':
578 			_uquad = UARG();
579 			base = DEC;
580 			goto nosign;
581 		case 'X':
582 			xdigs = "0123456789ABCDEF";
583 			goto hex;
584 		case 'x':
585 			xdigs = "0123456789abcdef";
586 hex:			_uquad = UARG();
587 			base = HEX;
588 			/* leading 0x/X only if non-zero */
589 			if (flags & ALT && _uquad != 0)
590 				flags |= HEXPREFIX;
591 
592 			/* unsigned conversions */
593 nosign:			sign = '\0';
594 			/*
595 			 * ``... diouXx conversions ... if a precision is
596 			 * specified, the 0 flag will be ignored.''
597 			 *	-- ANSI X3J11
598 			 */
599 number:			if ((dprec = prec) >= 0)
600 				flags &= ~ZEROPAD;
601 
602 			/*
603 			 * ``The result of converting a zero value with an
604 			 * explicit precision of zero is no characters.''
605 			 *	-- ANSI X3J11
606 			 */
607 			cp = buf + BUF;
608 			if (_uquad != 0 || prec != 0) {
609 				/*
610 				 * Unsigned mod is hard, and unsigned mod
611 				 * by a constant is easier than that by
612 				 * a variable; hence this switch.
613 				 */
614 				switch (base) {
615 				case OCT:
616 					do {
617 						*--cp = to_char(_uquad & 7);
618 						_uquad >>= 3;
619 					} while (_uquad);
620 					/* handle octal leading 0 */
621 					if (flags & ALT && *cp != '0')
622 						*--cp = '0';
623 					break;
624 
625 				case DEC:
626 					/* many numbers are 1 digit */
627 					while (_uquad >= 10) {
628 						*--cp = to_char(_uquad % 10);
629 						_uquad /= 10;
630 					}
631 					*--cp = to_char(_uquad);
632 					break;
633 
634 				case HEX:
635 					do {
636 						*--cp = xdigs[_uquad & 15];
637 						_uquad >>= 4;
638 					} while (_uquad);
639 					break;
640 
641 				default:
642 					cp = "bug in vfprintf: bad base";
643 					size = strlen(cp);
644 					goto skipsize;
645 				}
646 			}
647 			size = buf + BUF - cp;
648 		skipsize:
649 			break;
650 		default:	/* "%?" prints ?, unless ? is NUL */
651 			if (ch == '\0')
652 				goto done;
653 			/* pretend it was %c with argument ch */
654 			cp = buf;
655 			*cp = ch;
656 			size = 1;
657 			sign = '\0';
658 			break;
659 		}
660 
661 		/*
662 		 * All reasonable formats wind up here.  At this point, `cp'
663 		 * points to a string which (if not flags&LADJUST) should be
664 		 * padded out to `width' places.  If flags&ZEROPAD, it should
665 		 * first be prefixed by any sign or other prefix; otherwise,
666 		 * it should be blank padded before the prefix is emitted.
667 		 * After any left-hand padding and prefixing, emit zeroes
668 		 * required by a decimal [diouxX] precision, then print the
669 		 * string proper, then emit zeroes required by any leftover
670 		 * floating precision; finally, if LADJUST, pad with blanks.
671 		 *
672 		 * Compute actual size, so we know how much to pad.
673 		 * size excludes decimal prec; realsz includes it.
674 		 */
675 		realsz = dprec > size ? dprec : size;
676 		if (sign)
677 			realsz++;
678 		else if (flags & HEXPREFIX)
679 			realsz+= 2;
680 
681 		/* right-adjusting blank padding */
682 		if ((flags & (LADJUST|ZEROPAD)) == 0)
683 			PAD(width - realsz, blanks);
684 
685 		/* prefix */
686 		if (sign) {
687 			PRINT(&sign, 1);
688 		} else if (flags & HEXPREFIX) {
689 			ox[0] = '0';
690 			ox[1] = ch;
691 			PRINT(ox, 2);
692 		}
693 
694 		/* right-adjusting zero padding */
695 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
696 			PAD(width - realsz, zeroes);
697 
698 		/* leading zeroes from decimal precision */
699 		PAD(dprec - size, zeroes);
700 
701 		/* the string or number proper */
702 #ifdef FLOATING_POINT
703 		if ((flags & FPT) == 0) {
704 			PRINT(cp, size);
705 		} else {	/* glue together f_p fragments */
706 			if (ch >= 'f') {	/* 'f' or 'g' */
707 				if (_double == 0) {
708 					/* kludge for __dtoa irregularity */
709 					PRINT("0", 1);
710 					if (expt < ndig || (flags & ALT) != 0) {
711 						PRINT(decimal_point, 1);
712 						PAD(ndig - 1, zeroes);
713 					}
714 				} else if (expt <= 0) {
715 					PRINT("0", 1);
716 					PRINT(decimal_point, 1);
717 					PAD(-expt, zeroes);
718 					PRINT(cp, ndig);
719 				} else if (expt >= ndig) {
720 					PRINT(cp, ndig);
721 					PAD(expt - ndig, zeroes);
722 					if (flags & ALT)
723 						PRINT(".", 1);
724 				} else {
725 					PRINT(cp, expt);
726 					cp += expt;
727 					PRINT(".", 1);
728 					PRINT(cp, ndig-expt);
729 				}
730 			} else {	/* 'e' or 'E' */
731 				if (ndig > 1 || flags & ALT) {
732 					ox[0] = *cp++;
733 					ox[1] = '.';
734 					PRINT(ox, 2);
735 					if (_double) {
736 						PRINT(cp, ndig-1);
737 					} else	/* 0.[0..] */
738 						/* __dtoa irregularity */
739 						PAD(ndig - 1, zeroes);
740 				} else	/* XeYYY */
741 					PRINT(cp, 1);
742 				PRINT(expstr, expsize);
743 			}
744 		}
745 #else
746 		PRINT(cp, size);
747 #endif
748 		/* left-adjusting padding (always blank) */
749 		if (flags & LADJUST)
750 			PAD(width - realsz, blanks);
751 
752 		/* finally, adjust ret */
753 		ret += width > realsz ? width : realsz;
754 
755 		FLUSH();	/* copy out the I/O vectors */
756 	}
757 done:
758 	FLUSH();
759 error:
760 	if (argtable != NULL && argtable != statargtable)
761 		free(argtable);
762 	return (__sferror(fp) ? EOF : ret);
763 	/* NOTREACHED */
764 }
765 
766 /*
767  * Type ids for argument type table.
768  */
769 #define T_UNUSED	0
770 #define T_SHORT		1
771 #define T_U_SHORT	2
772 #define TP_SHORT	3
773 #define T_INT		4
774 #define T_U_INT		5
775 #define TP_INT		6
776 #define T_LONG		7
777 #define T_U_LONG	8
778 #define TP_LONG		9
779 #define T_QUAD		10
780 #define T_U_QUAD	11
781 #define TP_QUAD		12
782 #define T_DOUBLE	13
783 #define T_LONG_DOUBLE	14
784 #define TP_CHAR		15
785 #define TP_VOID		16
786 
787 /*
788  * Find all arguments when a positional parameter is encountered.  Returns a
789  * table, indexed by argument number, of pointers to each arguments.  The
790  * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
791  * It will be replaces with a malloc-ed on if it overflows.
792  */
793 static void
794 __find_arguments(fmt0, ap, argtable)
795 	const char *fmt0;
796 	va_list ap;
797 	va_list **argtable;
798 {
799 	register char *fmt;	/* format string */
800 	register int ch;	/* character from fmt */
801 	register int n, n2;	/* handy integer (short term usage) */
802 	register char *cp;	/* handy char pointer (short term usage) */
803 	register int flags;	/* flags as above */
804 	unsigned char *typetable; /* table of types */
805 	unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
806 	int tablesize;		/* current size of type table */
807 	int tablemax;		/* largest used index in table */
808 	int nextarg;		/* 1-based argument index */
809 
810 	/*
811 	 * Add an argument type to the table, expanding if necessary.
812 	 */
813 #define ADDTYPE(type) \
814 	((nextarg >= tablesize) ? \
815 		__grow_type_table(&typetable, &tablesize) : 0, \
816 	typetable[nextarg++] = type, \
817 	(nextarg > tablemax) ? tablemax = nextarg : 0)
818 
819 #define	ADDSARG() \
820 	((flags&LONGINT) ? ADDTYPE(T_LONG) : \
821 		((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
822 
823 #define	ADDUARG() \
824 	((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
825 		((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
826 
827 	/*
828 	 * Add * arguments to the type array.
829 	 */
830 #define ADDASTER() \
831 	n2 = 0; \
832 	cp = fmt; \
833 	while (is_digit(*cp)) { \
834 		n2 = 10 * n2 + to_digit(*cp); \
835 		cp++; \
836 	} \
837 	if (*cp == '$') { \
838 		int hold = nextarg; \
839 		nextarg = n2; \
840 		ADDTYPE(T_INT); \
841 		nextarg = hold; \
842 		fmt = ++cp; \
843 	} else { \
844 		ADDTYPE(T_INT); \
845 	}
846 	fmt = (char *)fmt0;
847 	typetable = stattypetable;
848 	tablesize = STATIC_ARG_TBL_SIZE;
849 	tablemax = 0;
850 	nextarg = 1;
851 	memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
852 
853 	/*
854 	 * Scan the format for conversions (`%' character).
855 	 */
856 	for (;;) {
857 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
858 			/* void */;
859 		if (ch == '\0')
860 			goto done;
861 		fmt++;		/* skip over '%' */
862 
863 		flags = 0;
864 
865 rflag:		ch = *fmt++;
866 reswitch:	switch (ch) {
867 		case ' ':
868 		case '#':
869 			goto rflag;
870 		case '*':
871 			ADDASTER();
872 			goto rflag;
873 		case '-':
874 		case '+':
875 			goto rflag;
876 		case '.':
877 			if ((ch = *fmt++) == '*') {
878 				ADDASTER();
879 				goto rflag;
880 			}
881 			while (is_digit(ch)) {
882 				ch = *fmt++;
883 			}
884 			goto reswitch;
885 		case '0':
886 			goto rflag;
887 		case '1': case '2': case '3': case '4':
888 		case '5': case '6': case '7': case '8': case '9':
889 			n = 0;
890 			do {
891 				n = 10 * n + to_digit(ch);
892 				ch = *fmt++;
893 			} while (is_digit(ch));
894 			if (ch == '$') {
895 				nextarg = n;
896 				goto rflag;
897 			}
898 			goto reswitch;
899 #ifdef FLOATING_POINT
900 		case 'L':
901 			flags |= LONGDBL;
902 			goto rflag;
903 #endif
904 		case 'h':
905 			flags |= SHORTINT;
906 			goto rflag;
907 		case 'l':
908 			flags |= LONGINT;
909 			goto rflag;
910 		case 'q':
911 			flags |= QUADINT;
912 			goto rflag;
913 		case 'c':
914 			ADDTYPE(T_INT);
915 			break;
916 		case 'D':
917 			flags |= LONGINT;
918 			/*FALLTHROUGH*/
919 		case 'd':
920 		case 'i':
921 			if (flags & QUADINT) {
922 				ADDTYPE(T_QUAD);
923 			} else {
924 				ADDSARG();
925 			}
926 			break;
927 #ifdef FLOATING_POINT
928 		case 'e':
929 		case 'E':
930 		case 'f':
931 		case 'g':
932 		case 'G':
933 			if (flags & LONGDBL)
934 				ADDTYPE(T_LONG_DOUBLE);
935 			else
936 				ADDTYPE(T_DOUBLE);
937 			break;
938 #endif /* FLOATING_POINT */
939 		case 'n':
940 			if (flags & QUADINT)
941 				ADDTYPE(TP_QUAD);
942 			else if (flags & LONGINT)
943 				ADDTYPE(TP_LONG);
944 			else if (flags & SHORTINT)
945 				ADDTYPE(TP_SHORT);
946 			else
947 				ADDTYPE(TP_INT);
948 			continue;	/* no output */
949 		case 'O':
950 			flags |= LONGINT;
951 			/*FALLTHROUGH*/
952 		case 'o':
953 			if (flags & QUADINT)
954 				ADDTYPE(T_U_QUAD);
955 			else
956 				ADDUARG();
957 			break;
958 		case 'p':
959 			ADDTYPE(TP_VOID);
960 			break;
961 		case 's':
962 			ADDTYPE(TP_CHAR);
963 			break;
964 		case 'U':
965 			flags |= LONGINT;
966 			/*FALLTHROUGH*/
967 		case 'u':
968 			if (flags & QUADINT)
969 				ADDTYPE(T_U_QUAD);
970 			else
971 				ADDUARG();
972 			break;
973 		case 'X':
974 		case 'x':
975 			if (flags & QUADINT)
976 				ADDTYPE(T_U_QUAD);
977 			else
978 				ADDUARG();
979 			break;
980 		default:	/* "%?" prints ?, unless ? is NUL */
981 			if (ch == '\0')
982 				goto done;
983 			break;
984 		}
985 	}
986 done:
987 	/*
988 	 * Build the argument table.
989 	 */
990 	if (tablemax >= STATIC_ARG_TBL_SIZE) {
991 		*argtable = (va_list *)
992 		    malloc(sizeof (va_list) * (tablemax + 1));
993 	}
994 
995 #if 0
996 	/* XXX is this required? */
997 	(*argtable) [0] = NULL;
998 #endif
999 	for (n = 1; n <= tablemax; n++) {
1000 		(*argtable)[n] = ap;
1001 		switch (typetable[n]) {
1002 		case T_UNUSED:
1003 			(void) va_arg(ap, int);
1004 			break;
1005 		case T_SHORT:
1006 			(void) va_arg(ap, int);
1007 			break;
1008 		case T_U_SHORT:
1009 			(void) va_arg(ap, int);
1010 			break;
1011 		case TP_SHORT:
1012 			(void) va_arg(ap, short *);
1013 			break;
1014 		case T_INT:
1015 			(void) va_arg(ap, int);
1016 			break;
1017 		case T_U_INT:
1018 			(void) va_arg(ap, unsigned int);
1019 			break;
1020 		case TP_INT:
1021 			(void) va_arg(ap, int *);
1022 			break;
1023 		case T_LONG:
1024 			(void) va_arg(ap, long);
1025 			break;
1026 		case T_U_LONG:
1027 			(void) va_arg(ap, unsigned long);
1028 			break;
1029 		case TP_LONG:
1030 			(void) va_arg(ap, long *);
1031 			break;
1032 		case T_QUAD:
1033 			(void) va_arg(ap, quad_t);
1034 			break;
1035 		case T_U_QUAD:
1036 			(void) va_arg(ap, u_quad_t);
1037 			break;
1038 		case TP_QUAD:
1039 			(void) va_arg(ap, quad_t *);
1040 			break;
1041 		case T_DOUBLE:
1042 			(void) va_arg(ap, double);
1043 			break;
1044 		case T_LONG_DOUBLE:
1045 			(void) va_arg(ap, long double);
1046 			break;
1047 		case TP_CHAR:
1048 			(void) va_arg(ap, char *);
1049 			break;
1050 		case TP_VOID:
1051 			(void) va_arg(ap, void *);
1052 			break;
1053 		}
1054 	}
1055 
1056 	if (typetable != NULL && typetable != stattypetable)
1057 		free(typetable);
1058 }
1059 
1060 /*
1061  * Increase the size of the type table.
1062  */
1063 static int
1064 __grow_type_table(typetable, tablesize)
1065 	unsigned char **typetable;
1066 	int *tablesize;
1067 {
1068 	unsigned char *oldtable = *typetable;
1069 	int newsize = *tablesize * 2;
1070 
1071 	if (*tablesize == STATIC_ARG_TBL_SIZE) {
1072 		*typetable = (unsigned char *)
1073 		    malloc(sizeof (unsigned char) * newsize);
1074 		/* XXX unchecked */
1075 		bcopy(oldtable, *typetable, *tablesize);
1076 	} else {
1077 		*typetable = (unsigned char *)
1078 		    realloc(*typetable, sizeof (unsigned char) * newsize);
1079 		/* XXX unchecked */
1080 	}
1081 	memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
1082 
1083 	*tablesize = newsize;
1084 	return(0);
1085 }
1086 
1087 
1088 #ifdef FLOATING_POINT
1089 
1090 extern char *__dtoa(double, int, int, int *, int *, char **);
1091 
1092 static char *
1093 cvt(value, ndigits, flags, sign, decpt, ch, length)
1094 	double value;
1095 	int ndigits, flags, *decpt, ch, *length;
1096 	char *sign;
1097 {
1098 	int mode, dsgn;
1099 	char *digits, *bp, *rve;
1100 
1101 	if (ch == 'f') {
1102 		mode = 3;		/* ndigits after the decimal point */
1103 	} else {
1104 		/* To obtain ndigits after the decimal point for the 'e'
1105 		 * and 'E' formats, round to ndigits + 1 significant
1106 		 * figures.
1107 		 */
1108 		if (ch == 'e' || ch == 'E') {
1109 			ndigits++;
1110 		}
1111 		mode = 2;		/* ndigits significant digits */
1112 	}
1113 
1114 	if (value < 0) {
1115 		value = -value;
1116 		*sign = '-';
1117 	} else
1118 		*sign = '\000';
1119 	digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
1120 	if ((ch != 'g' && ch != 'G') || flags & ALT) {	/* Print trailing zeros */
1121 		bp = digits + ndigits;
1122 		if (ch == 'f') {
1123 			if (*digits == '0' && value)
1124 				*decpt = -ndigits + 1;
1125 			bp += *decpt;
1126 		}
1127 		if (value == 0)	/* kludge for __dtoa irregularity */
1128 			rve = bp;
1129 		while (rve < bp)
1130 			*rve++ = '0';
1131 	}
1132 	*length = rve - digits;
1133 	return (digits);
1134 }
1135 
1136 static int
1137 exponent(p0, exp, fmtch)
1138 	char *p0;
1139 	int exp, fmtch;
1140 {
1141 	register char *p, *t;
1142 	char expbuf[MAXEXP];
1143 
1144 	p = p0;
1145 	*p++ = fmtch;
1146 	if (exp < 0) {
1147 		exp = -exp;
1148 		*p++ = '-';
1149 	}
1150 	else
1151 		*p++ = '+';
1152 	t = expbuf + MAXEXP;
1153 	if (exp > 9) {
1154 		do {
1155 			*--t = to_char(exp % 10);
1156 		} while ((exp /= 10) > 9);
1157 		*--t = to_char(exp);
1158 		for (; t < expbuf + MAXEXP; *p++ = *t++);
1159 	}
1160 	else {
1161 		*p++ = '0';
1162 		*p++ = to_char(exp);
1163 	}
1164 	return (p - p0);
1165 }
1166 #endif /* FLOATING_POINT */
1167