xref: /openbsd-src/lib/libc/stdio/vfprintf.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: vfprintf.c,v 1.41 2007/01/16 19:20:53 millert 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 			flags |= SHORTINT;
450 			goto rflag;
451 		case 'j':
452 			flags |= MAXINT;
453 			goto rflag;
454 		case 'l':
455 			if (*fmt == 'l') {
456 				fmt++;
457 				flags |= LLONGINT;
458 			} else {
459 				flags |= LONGINT;
460 			}
461 			goto rflag;
462 		case 'q':
463 			flags |= LLONGINT;
464 			goto rflag;
465 		case 't':
466 			flags |= PTRINT;
467 			goto rflag;
468 		case 'z':
469 			flags |= SIZEINT;
470 			goto rflag;
471 		case 'c':
472 			*(cp = buf) = GETARG(int);
473 			size = 1;
474 			sign = '\0';
475 			break;
476 		case 'D':
477 			flags |= LONGINT;
478 			/*FALLTHROUGH*/
479 		case 'd':
480 		case 'i':
481 			_umax = SARG();
482 			if ((intmax_t)_umax < 0) {
483 				_umax = -_umax;
484 				sign = '-';
485 			}
486 			base = DEC;
487 			goto number;
488 #ifdef FLOATING_POINT
489 		case 'e':
490 		case 'E':
491 		case 'f':
492 		case 'g':
493 		case 'G':
494 			if (prec == -1) {
495 				prec = DEFPREC;
496 			} else if ((ch == 'g' || ch == 'G') && prec == 0) {
497 				prec = 1;
498 			}
499 
500 			if (flags & LONGDBL) {
501 				_double = (double) GETARG(long double);
502 			} else {
503 				_double = GETARG(double);
504 			}
505 
506 			/* do this before tricky precision changes */
507 			if (isinf(_double)) {
508 				if (_double < 0)
509 					sign = '-';
510 				cp = "Inf";
511 				size = 3;
512 				break;
513 			}
514 			if (isnan(_double)) {
515 				cp = "NaN";
516 				size = 3;
517 				break;
518 			}
519 
520 			flags |= FPT;
521 			if (dtoaresult)
522 				__freedtoa(dtoaresult);
523 			dtoaresult = cp = cvt(_double, prec, flags, &softsign,
524 				&expt, ch, &ndig);
525 			if (ch == 'g' || ch == 'G') {
526 				if (expt <= -4 || expt > prec)
527 					ch = (ch == 'g') ? 'e' : 'E';
528 				else
529 					ch = 'g';
530 			}
531 			if (ch <= 'e') {	/* 'e' or 'E' fmt */
532 				--expt;
533 				expsize = exponent(expstr, expt, ch);
534 				size = expsize + ndig;
535 				if (ndig > 1 || flags & ALT)
536 					++size;
537 			} else if (ch == 'f') {		/* f fmt */
538 				if (expt > 0) {
539 					size = expt;
540 					if (prec || flags & ALT)
541 						size += prec + 1;
542 				} else { /* "0.X" */
543 					size = prec + 2;
544 				}
545 			} else if (expt >= ndig) {	/* fixed g fmt */
546 				size = expt;
547 				if (flags & ALT)
548 					++size;
549 			} else {
550 				size = ndig + (expt > 0 ?  1 : 2 - expt);
551 			}
552 
553 			if (softsign)
554 				sign = '-';
555 			break;
556 #endif /* FLOATING_POINT */
557 		case 'n':
558 			if (flags & LLONGINT)
559 				*GETARG(long long *) = ret;
560 			else if (flags & LONGINT)
561 				*GETARG(long *) = ret;
562 			else if (flags & SHORTINT)
563 				*GETARG(short *) = ret;
564 			else if (flags & CHARINT)
565 				*GETARG(__signed char *) = ret;
566 			else if (flags & PTRINT)
567 				*GETARG(ptrdiff_t *) = ret;
568 			else if (flags & SIZEINT)
569 				*GETARG(ssize_t *) = ret;
570 			else if (flags & MAXINT)
571 				*GETARG(intmax_t *) = ret;
572 			else
573 				*GETARG(int *) = ret;
574 			continue;	/* no output */
575 		case 'O':
576 			flags |= LONGINT;
577 			/*FALLTHROUGH*/
578 		case 'o':
579 			_umax = UARG();
580 			base = OCT;
581 			goto nosign;
582 		case 'p':
583 			/*
584 			 * ``The argument shall be a pointer to void.  The
585 			 * value of the pointer is converted to a sequence
586 			 * of printable characters, in an implementation-
587 			 * defined manner.''
588 			 *	-- ANSI X3J11
589 			 */
590 			/* NOSTRICT */
591 			_umax = (u_long)GETARG(void *);
592 			base = HEX;
593 			xdigs = "0123456789abcdef";
594 			flags |= HEXPREFIX;
595 			ch = 'x';
596 			goto nosign;
597 		case 's':
598 			if ((cp = GETARG(char *)) == NULL)
599 				cp = "(null)";
600 			if (prec >= 0) {
601 				/*
602 				 * can't use strlen; can only look for the
603 				 * NUL in the first `prec' characters, and
604 				 * strlen() will go further.
605 				 */
606 				char *p = memchr(cp, 0, prec);
607 
608 				if (p != NULL) {
609 					size = p - cp;
610 					if (size > prec)
611 						size = prec;
612 				} else {
613 					size = prec;
614 				}
615 			} else {
616 				size = strlen(cp);
617 			}
618 			sign = '\0';
619 			break;
620 		case 'U':
621 			flags |= LONGINT;
622 			/*FALLTHROUGH*/
623 		case 'u':
624 			_umax = UARG();
625 			base = DEC;
626 			goto nosign;
627 		case 'X':
628 			xdigs = "0123456789ABCDEF";
629 			goto hex;
630 		case 'x':
631 			xdigs = "0123456789abcdef";
632 hex:			_umax = UARG();
633 			base = HEX;
634 			/* leading 0x/X only if non-zero */
635 			if (flags & ALT && _umax != 0)
636 				flags |= HEXPREFIX;
637 
638 			/* unsigned conversions */
639 nosign:			sign = '\0';
640 			/*
641 			 * ``... diouXx conversions ... if a precision is
642 			 * specified, the 0 flag will be ignored.''
643 			 *	-- ANSI X3J11
644 			 */
645 number:			if ((dprec = prec) >= 0)
646 				flags &= ~ZEROPAD;
647 
648 			/*
649 			 * ``The result of converting a zero value with an
650 			 * explicit precision of zero is no characters.''
651 			 *	-- ANSI X3J11
652 			 */
653 			cp = buf + BUF;
654 			if (_umax != 0 || prec != 0) {
655 				/*
656 				 * Unsigned mod is hard, and unsigned mod
657 				 * by a constant is easier than that by
658 				 * a variable; hence this switch.
659 				 */
660 				switch (base) {
661 				case OCT:
662 					do {
663 						*--cp = to_char(_umax & 7);
664 						_umax >>= 3;
665 					} while (_umax);
666 					/* handle octal leading 0 */
667 					if (flags & ALT && *cp != '0')
668 						*--cp = '0';
669 					break;
670 
671 				case DEC:
672 					/* many numbers are 1 digit */
673 					while (_umax >= 10) {
674 						*--cp = to_char(_umax % 10);
675 						_umax /= 10;
676 					}
677 					*--cp = to_char(_umax);
678 					break;
679 
680 				case HEX:
681 					do {
682 						*--cp = xdigs[_umax & 15];
683 						_umax >>= 4;
684 					} while (_umax);
685 					break;
686 
687 				default:
688 					cp = "bug in vfprintf: bad base";
689 					size = strlen(cp);
690 					goto skipsize;
691 				}
692 			}
693 			size = buf + BUF - cp;
694 		skipsize:
695 			break;
696 		default:	/* "%?" prints ?, unless ? is NUL */
697 			if (ch == '\0')
698 				goto done;
699 			/* pretend it was %c with argument ch */
700 			cp = buf;
701 			*cp = ch;
702 			size = 1;
703 			sign = '\0';
704 			break;
705 		}
706 
707 		/*
708 		 * All reasonable formats wind up here.  At this point, `cp'
709 		 * points to a string which (if not flags&LADJUST) should be
710 		 * padded out to `width' places.  If flags&ZEROPAD, it should
711 		 * first be prefixed by any sign or other prefix; otherwise,
712 		 * it should be blank padded before the prefix is emitted.
713 		 * After any left-hand padding and prefixing, emit zeroes
714 		 * required by a decimal [diouxX] precision, then print the
715 		 * string proper, then emit zeroes required by any leftover
716 		 * floating precision; finally, if LADJUST, pad with blanks.
717 		 *
718 		 * Compute actual size, so we know how much to pad.
719 		 * size excludes decimal prec; realsz includes it.
720 		 */
721 		realsz = dprec > size ? dprec : size;
722 		if (sign)
723 			realsz++;
724 		else if (flags & HEXPREFIX)
725 			realsz+= 2;
726 
727 		/* right-adjusting blank padding */
728 		if ((flags & (LADJUST|ZEROPAD)) == 0)
729 			PAD(width - realsz, blanks);
730 
731 		/* prefix */
732 		if (sign) {
733 			PRINT(&sign, 1);
734 		} else if (flags & HEXPREFIX) {
735 			ox[0] = '0';
736 			ox[1] = ch;
737 			PRINT(ox, 2);
738 		}
739 
740 		/* right-adjusting zero padding */
741 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
742 			PAD(width - realsz, zeroes);
743 
744 		/* leading zeroes from decimal precision */
745 		PAD(dprec - size, zeroes);
746 
747 		/* the string or number proper */
748 #ifdef FLOATING_POINT
749 		if ((flags & FPT) == 0) {
750 			PRINT(cp, size);
751 		} else {	/* glue together f_p fragments */
752 			if (ch >= 'f') {	/* 'f' or 'g' */
753 				if (_double == 0) {
754 					/* kludge for __dtoa irregularity */
755 					PRINT("0", 1);
756 					if (expt < ndig || (flags & ALT) != 0) {
757 						PRINT(decimal_point, 1);
758 						PAD(ndig - 1, zeroes);
759 					}
760 				} else if (expt <= 0) {
761 					PRINT("0", 1);
762 					PRINT(decimal_point, 1);
763 					PAD(-expt, zeroes);
764 					PRINT(cp, ndig);
765 				} else if (expt >= ndig) {
766 					PRINT(cp, ndig);
767 					PAD(expt - ndig, zeroes);
768 					if (flags & ALT)
769 						PRINT(".", 1);
770 				} else {
771 					PRINT(cp, expt);
772 					cp += expt;
773 					PRINT(".", 1);
774 					PRINT(cp, ndig-expt);
775 				}
776 			} else {	/* 'e' or 'E' */
777 				if (ndig > 1 || flags & ALT) {
778 					ox[0] = *cp++;
779 					ox[1] = '.';
780 					PRINT(ox, 2);
781 					if (_double) {
782 						PRINT(cp, ndig-1);
783 					} else {/* 0.[0..] */
784 						/* __dtoa irregularity */
785 						PAD(ndig - 1, zeroes);
786 					}
787 				} else { /* XeYYY */
788 					PRINT(cp, 1);
789 				}
790 				PRINT(expstr, expsize);
791 			}
792 		}
793 #else
794 		PRINT(cp, size);
795 #endif
796 		/* left-adjusting padding (always blank) */
797 		if (flags & LADJUST)
798 			PAD(width - realsz, blanks);
799 
800 		/* finally, adjust ret */
801 		if (width < realsz)
802 			width = realsz;
803 		if (width > INT_MAX - ret)
804 			goto overflow;
805 		ret += width;
806 
807 		FLUSH();	/* copy out the I/O vectors */
808 	}
809 done:
810 	FLUSH();
811 error:
812 	if (__sferror(fp))
813 		ret = -1;
814 	goto finish;
815 
816 overflow:
817 	errno = ENOMEM;
818 	ret = -1;
819 
820 finish:
821 #ifdef FLOATING_POINT
822 	if (dtoaresult)
823 		__freedtoa(dtoaresult);
824 #endif
825 	if (argtable != NULL && argtable != statargtable) {
826 		munmap(argtable, argtablesiz);
827 		argtable = NULL;
828 	}
829 	return (ret);
830 }
831 
832 /*
833  * Type ids for argument type table.
834  */
835 #define T_UNUSED	0
836 #define T_SHORT		1
837 #define T_U_SHORT	2
838 #define TP_SHORT	3
839 #define T_INT		4
840 #define T_U_INT		5
841 #define TP_INT		6
842 #define T_LONG		7
843 #define T_U_LONG	8
844 #define TP_LONG		9
845 #define T_LLONG		10
846 #define T_U_LLONG	11
847 #define TP_LLONG	12
848 #define T_DOUBLE	13
849 #define T_LONG_DOUBLE	14
850 #define TP_CHAR		15
851 #define TP_VOID		16
852 #define T_PTRINT	17
853 #define TP_PTRINT	18
854 #define T_SIZEINT	19
855 #define T_SSIZEINT	20
856 #define TP_SSIZEINT	21
857 #define T_MAXINT	22
858 #define T_MAXUINT	23
859 #define TP_MAXINT	24
860 
861 /*
862  * Find all arguments when a positional parameter is encountered.  Returns a
863  * table, indexed by argument number, of pointers to each arguments.  The
864  * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
865  * It will be replaced with a mmap-ed one if it overflows (malloc cannot be
866  * used since we are attempting to make snprintf thread safe, and alloca is
867  * problematic since we have nested functions..)
868  */
869 static int
870 __find_arguments(const char *fmt0, va_list ap, va_list **argtable,
871     size_t *argtablesiz)
872 {
873 	char *fmt;		/* format string */
874 	int ch;			/* character from fmt */
875 	int n, n2;		/* handy integer (short term usage) */
876 	char *cp;		/* handy char pointer (short term usage) */
877 	int flags;		/* flags as above */
878 	unsigned char *typetable; /* table of types */
879 	unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
880 	int tablesize;		/* current size of type table */
881 	int tablemax;		/* largest used index in table */
882 	int nextarg;		/* 1-based argument index */
883 	int ret = 0;		/* return value */
884 	wchar_t wc;
885 	mbstate_t ps;
886 
887 	/*
888 	 * Add an argument type to the table, expanding if necessary.
889 	 */
890 #define ADDTYPE(type) \
891 	((nextarg >= tablesize) ? \
892 		__grow_type_table(&typetable, &tablesize) : 0, \
893 	(nextarg > tablemax) ? tablemax = nextarg : 0, \
894 	typetable[nextarg++] = type)
895 
896 #define	ADDSARG() \
897         ((flags&MAXINT) ? ADDTYPE(T_MAXINT) : \
898 	    ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
899 	    ((flags&SIZEINT) ? ADDTYPE(T_SSIZEINT) : \
900 	    ((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \
901 	    ((flags&LONGINT) ? ADDTYPE(T_LONG) : \
902 	    ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))))))
903 
904 #define	ADDUARG() \
905         ((flags&MAXINT) ? ADDTYPE(T_MAXUINT) : \
906 	    ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
907 	    ((flags&SIZEINT) ? ADDTYPE(T_SIZEINT) : \
908 	    ((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \
909 	    ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
910 	    ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))))))
911 
912 	/*
913 	 * Add * arguments to the type array.
914 	 */
915 #define ADDASTER() \
916 	n2 = 0; \
917 	cp = fmt; \
918 	while (is_digit(*cp)) { \
919 		APPEND_DIGIT(n2, *cp); \
920 		cp++; \
921 	} \
922 	if (*cp == '$') { \
923 		int hold = nextarg; \
924 		nextarg = n2; \
925 		ADDTYPE(T_INT); \
926 		nextarg = hold; \
927 		fmt = ++cp; \
928 	} else { \
929 		ADDTYPE(T_INT); \
930 	}
931 	fmt = (char *)fmt0;
932 	typetable = stattypetable;
933 	tablesize = STATIC_ARG_TBL_SIZE;
934 	tablemax = 0;
935 	nextarg = 1;
936 	memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
937 	memset(&ps, 0, sizeof(ps));
938 
939 	/*
940 	 * Scan the format for conversions (`%' character).
941 	 */
942 	for (;;) {
943 		cp = fmt;
944 		while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0) {
945 			fmt += n;
946 			if (wc == '%') {
947 				fmt--;
948 				break;
949 			}
950 		}
951 		if (n <= 0)
952 			goto done;
953 		fmt++;		/* skip over '%' */
954 
955 		flags = 0;
956 
957 rflag:		ch = *fmt++;
958 reswitch:	switch (ch) {
959 		case ' ':
960 		case '#':
961 			goto rflag;
962 		case '*':
963 			ADDASTER();
964 			goto rflag;
965 		case '-':
966 		case '+':
967 			goto rflag;
968 		case '.':
969 			if ((ch = *fmt++) == '*') {
970 				ADDASTER();
971 				goto rflag;
972 			}
973 			while (is_digit(ch)) {
974 				ch = *fmt++;
975 			}
976 			goto reswitch;
977 		case '0':
978 			goto rflag;
979 		case '1': case '2': case '3': case '4':
980 		case '5': case '6': case '7': case '8': case '9':
981 			n = 0;
982 			do {
983 				APPEND_DIGIT(n ,ch);
984 				ch = *fmt++;
985 			} while (is_digit(ch));
986 			if (ch == '$') {
987 				nextarg = n;
988 				goto rflag;
989 			}
990 			goto reswitch;
991 #ifdef FLOATING_POINT
992 		case 'L':
993 			flags |= LONGDBL;
994 			goto rflag;
995 #endif
996 		case 'h':
997 			if (*fmt == 'h') {
998 				fmt++;
999 				flags |= CHARINT;
1000 			} else {
1001 				flags |= SHORTINT;
1002 			}
1003 			goto rflag;
1004 		case 'l':
1005 			if (*fmt == 'l') {
1006 				fmt++;
1007 				flags |= LLONGINT;
1008 			} else {
1009 				flags |= LONGINT;
1010 			}
1011 			goto rflag;
1012 		case 'q':
1013 			flags |= LLONGINT;
1014 			goto rflag;
1015 		case 't':
1016 			flags |= PTRINT;
1017 			goto rflag;
1018 		case 'z':
1019 			flags |= SIZEINT;
1020 			goto rflag;
1021 		case 'c':
1022 			ADDTYPE(T_INT);
1023 			break;
1024 		case 'D':
1025 			flags |= LONGINT;
1026 			/*FALLTHROUGH*/
1027 		case 'd':
1028 		case 'i':
1029 			ADDSARG();
1030 			break;
1031 #ifdef FLOATING_POINT
1032 		case 'e':
1033 		case 'E':
1034 		case 'f':
1035 		case 'g':
1036 		case 'G':
1037 			if (flags & LONGDBL)
1038 				ADDTYPE(T_LONG_DOUBLE);
1039 			else
1040 				ADDTYPE(T_DOUBLE);
1041 			break;
1042 #endif /* FLOATING_POINT */
1043 		case 'n':
1044 			if (flags & LLONGINT)
1045 				ADDTYPE(TP_LLONG);
1046 			else if (flags & LONGINT)
1047 				ADDTYPE(TP_LONG);
1048 			else if (flags & SHORTINT)
1049 				ADDTYPE(TP_SHORT);
1050 			else if (flags & PTRINT)
1051 				ADDTYPE(TP_PTRINT);
1052 			else if (flags & SIZEINT)
1053 				ADDTYPE(TP_SSIZEINT);
1054 			else if (flags & MAXINT)
1055 				ADDTYPE(TP_MAXINT);
1056 			else
1057 				ADDTYPE(TP_INT);
1058 			continue;	/* no output */
1059 		case 'O':
1060 			flags |= LONGINT;
1061 			/*FALLTHROUGH*/
1062 		case 'o':
1063 			ADDUARG();
1064 			break;
1065 		case 'p':
1066 			ADDTYPE(TP_VOID);
1067 			break;
1068 		case 's':
1069 			ADDTYPE(TP_CHAR);
1070 			break;
1071 		case 'U':
1072 			flags |= LONGINT;
1073 			/*FALLTHROUGH*/
1074 		case 'u':
1075 		case 'X':
1076 		case 'x':
1077 			ADDUARG();
1078 			break;
1079 		default:	/* "%?" prints ?, unless ? is NUL */
1080 			if (ch == '\0')
1081 				goto done;
1082 			break;
1083 		}
1084 	}
1085 done:
1086 	/*
1087 	 * Build the argument table.
1088 	 */
1089 	if (tablemax >= STATIC_ARG_TBL_SIZE) {
1090 		*argtablesiz = sizeof (va_list) * (tablemax + 1);
1091 		*argtable = mmap(NULL, *argtablesiz,
1092 		    PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
1093 		if (*argtable == MAP_FAILED)
1094 			return (-1);
1095 	}
1096 
1097 #if 0
1098 	/* XXX is this required? */
1099 	(*argtable) [0] = NULL;
1100 #endif
1101 	for (n = 1; n <= tablemax; n++) {
1102 		va_copy((*argtable)[n], ap);
1103 		switch (typetable[n]) {
1104 		case T_UNUSED:
1105 			(void) va_arg(ap, int);
1106 			break;
1107 		case T_SHORT:
1108 			(void) va_arg(ap, int);
1109 			break;
1110 		case T_U_SHORT:
1111 			(void) va_arg(ap, int);
1112 			break;
1113 		case TP_SHORT:
1114 			(void) va_arg(ap, short *);
1115 			break;
1116 		case T_INT:
1117 			(void) va_arg(ap, int);
1118 			break;
1119 		case T_U_INT:
1120 			(void) va_arg(ap, unsigned int);
1121 			break;
1122 		case TP_INT:
1123 			(void) va_arg(ap, int *);
1124 			break;
1125 		case T_LONG:
1126 			(void) va_arg(ap, long);
1127 			break;
1128 		case T_U_LONG:
1129 			(void) va_arg(ap, unsigned long);
1130 			break;
1131 		case TP_LONG:
1132 			(void) va_arg(ap, long *);
1133 			break;
1134 		case T_LLONG:
1135 			(void) va_arg(ap, long long);
1136 			break;
1137 		case T_U_LLONG:
1138 			(void) va_arg(ap, unsigned long long);
1139 			break;
1140 		case TP_LLONG:
1141 			(void) va_arg(ap, long long *);
1142 			break;
1143 		case T_DOUBLE:
1144 			(void) va_arg(ap, double);
1145 			break;
1146 		case T_LONG_DOUBLE:
1147 			(void) va_arg(ap, long double);
1148 			break;
1149 		case TP_CHAR:
1150 			(void) va_arg(ap, char *);
1151 			break;
1152 		case TP_VOID:
1153 			(void) va_arg(ap, void *);
1154 			break;
1155 		case T_PTRINT:
1156 			(void) va_arg(ap, ptrdiff_t);
1157 			break;
1158 		case TP_PTRINT:
1159 			(void) va_arg(ap, ptrdiff_t *);
1160 			break;
1161 		case T_SIZEINT:
1162 			(void) va_arg(ap, size_t);
1163 			break;
1164 		case T_SSIZEINT:
1165 			(void) va_arg(ap, ssize_t);
1166 			break;
1167 		case TP_SSIZEINT:
1168 			(void) va_arg(ap, ssize_t *);
1169 			break;
1170 		case TP_MAXINT:
1171 			(void) va_arg(ap, intmax_t *);
1172 			break;
1173 		}
1174 	}
1175 	goto finish;
1176 
1177 overflow:
1178 	errno = ENOMEM;
1179 	ret = -1;
1180 
1181 finish:
1182 	if (typetable != NULL && typetable != stattypetable) {
1183 		munmap(typetable, *argtablesiz);
1184 		typetable = NULL;
1185 	}
1186 	return (ret);
1187 }
1188 
1189 /*
1190  * Increase the size of the type table.
1191  */
1192 static int
1193 __grow_type_table(unsigned char **typetable, int *tablesize)
1194 {
1195 	unsigned char *oldtable = *typetable;
1196 	int newsize = *tablesize * 2;
1197 
1198 	if (newsize < getpagesize())
1199 		newsize = getpagesize();
1200 
1201 	if (*tablesize == STATIC_ARG_TBL_SIZE) {
1202 		*typetable = mmap(NULL, newsize, PROT_WRITE|PROT_READ,
1203 		    MAP_ANON|MAP_PRIVATE, -1, 0);
1204 		if (*typetable == MAP_FAILED)
1205 			return (-1);
1206 		bcopy(oldtable, *typetable, *tablesize);
1207 	} else {
1208 		unsigned char *new = mmap(NULL, newsize, PROT_WRITE|PROT_READ,
1209 		    MAP_ANON|MAP_PRIVATE, -1, 0);
1210 		if (new == MAP_FAILED)
1211 			return (-1);
1212 		memmove(new, *typetable, *tablesize);
1213 		munmap(*typetable, *tablesize);
1214 		*typetable = new;
1215 	}
1216 	memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
1217 
1218 	*tablesize = newsize;
1219 	return (0);
1220 }
1221 
1222 
1223 #ifdef FLOATING_POINT
1224 
1225 static char *
1226 cvt(double value, int ndigits, int flags, char *sign, int *decpt, int ch,
1227     int *length)
1228 {
1229 	int mode, dsgn;
1230 	char *digits, *bp, *rve;
1231 
1232 	if (ch == 'f') {
1233 		mode = 3;		/* ndigits after the decimal point */
1234 	} else {
1235 		/* To obtain ndigits after the decimal point for the 'e'
1236 		 * and 'E' formats, round to ndigits + 1 significant
1237 		 * figures.
1238 		 */
1239 		if (ch == 'e' || ch == 'E') {
1240 			ndigits++;
1241 		}
1242 		mode = 2;		/* ndigits significant digits */
1243 	}
1244 
1245 	if (value < 0) {
1246 		value = -value;
1247 		*sign = '-';
1248 	} else
1249 		*sign = '\000';
1250 	digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
1251 	if ((ch != 'g' && ch != 'G') || flags & ALT) {/* Print trailing zeros */
1252 		bp = digits + ndigits;
1253 		if (ch == 'f') {
1254 			if (*digits == '0' && value)
1255 				*decpt = -ndigits + 1;
1256 			bp += *decpt;
1257 		}
1258 		if (value == 0)	/* kludge for __dtoa irregularity */
1259 			rve = bp;
1260 		while (rve < bp)
1261 			*rve++ = '0';
1262 	}
1263 	*length = rve - digits;
1264 	return (digits);
1265 }
1266 
1267 static int
1268 exponent(char *p0, int exp, int fmtch)
1269 {
1270 	char *p, *t;
1271 	char expbuf[MAXEXP];
1272 
1273 	p = p0;
1274 	*p++ = fmtch;
1275 	if (exp < 0) {
1276 		exp = -exp;
1277 		*p++ = '-';
1278 	} else
1279 		*p++ = '+';
1280 	t = expbuf + MAXEXP;
1281 	if (exp > 9) {
1282 		do {
1283 			*--t = to_char(exp % 10);
1284 		} while ((exp /= 10) > 9);
1285 		*--t = to_char(exp);
1286 		for (; t < expbuf + MAXEXP; *p++ = *t++)
1287 			/* nothing */;
1288 	} else {
1289 		*p++ = '0';
1290 		*p++ = to_char(exp);
1291 	}
1292 	return (p - p0);
1293 }
1294 #endif /* FLOATING_POINT */
1295