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