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