|  | /*- | 
|  | * Copyright (c) 1990 The Regents of the University of California. | 
|  | * All rights reserved. | 
|  | * | 
|  | * This code is derived from software contributed to Berkeley by | 
|  | * Chris Torek. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. Neither the name of the University nor the names of its contributors | 
|  | *    may be used to endorse or promote products derived from this software | 
|  | *    without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
|  | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
|  | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | * SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include <sys/mman.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <float.h> | 
|  | #include <langinfo.h> | 
|  | #include <limits.h> | 
|  | #include <locale.h> | 
|  | #include <math.h> | 
|  | #include <stdarg.h> | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <wchar.h> | 
|  |  | 
|  | #include <platform/bionic/macros.h> | 
|  |  | 
|  | #include "fvwrite.h" | 
|  | #include "gdtoa.h" | 
|  | #include "local.h" | 
|  |  | 
|  | union arg { | 
|  | int intarg; | 
|  | unsigned int uintarg; | 
|  | long longarg; | 
|  | unsigned long ulongarg; | 
|  | long long longlongarg; | 
|  | unsigned long long ulonglongarg; | 
|  | ptrdiff_t ptrdiffarg; | 
|  | size_t sizearg; | 
|  | ssize_t ssizearg; | 
|  | intmax_t intmaxarg; | 
|  | uintmax_t uintmaxarg; | 
|  | void* pvoidarg; | 
|  | char* pchararg; | 
|  | signed char* pschararg; | 
|  | short* pshortarg; | 
|  | int* pintarg; | 
|  | long* plongarg; | 
|  | long long* plonglongarg; | 
|  | ptrdiff_t* pptrdiffarg; | 
|  | ssize_t* pssizearg; | 
|  | intmax_t* pintmaxarg; | 
|  | double doublearg; | 
|  | long double longdoublearg; | 
|  | wint_t wintarg; | 
|  | wchar_t* pwchararg; | 
|  | }; | 
|  |  | 
|  | // Helper function for `fprintf to unbuffered unix file': creates a | 
|  | // temporary buffer.  We only work on write-only files; this avoids | 
|  | // worries about ungetc buffers and so forth. | 
|  | static int __sbprintf(FILE* fp, const CHAR_TYPE* fmt, va_list ap) { | 
|  | FILE fake; | 
|  | struct __sfileext fakeext; | 
|  | unsigned char buf[BUFSIZ]; | 
|  |  | 
|  | _FILEEXT_SETUP(&fake, &fakeext); | 
|  | /* copy the important variables */ | 
|  | fake._flags = fp->_flags & ~__SNBF; | 
|  | fake._file = fp->_file; | 
|  | fake._cookie = fp->_cookie; | 
|  | fake._write = fp->_write; | 
|  |  | 
|  | /* set up the buffer */ | 
|  | fake._bf._base = fake._p = buf; | 
|  | fake._bf._size = fake._w = sizeof(buf); | 
|  | fake._lbfsize = 0; /* not actually used, but Just In Case */ | 
|  |  | 
|  | /* do the work, then copy any error status */ | 
|  | int ret = FUNCTION_NAME(&fake, fmt, ap); | 
|  | if (ret >= 0 && __sflush(&fake)) ret = EOF; | 
|  | if (fake._flags & __SERR) fp->_flags |= __SERR; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, size_t* argtablesiz); | 
|  | static int __grow_type_table(unsigned char** typetable, int* tablesize); | 
|  |  | 
|  | #define DEFPREC 6 | 
|  |  | 
|  | #define to_digit(c) ((c) - '0') | 
|  | #define is_digit(c) ((unsigned)to_digit(c) <= 9) | 
|  | #define to_char(n) ((CHAR_TYPE)((n) + '0')) | 
|  |  | 
|  | template <typename CharT> | 
|  | static int exponent(CharT* p0, int exp, int fmtch) { | 
|  | CharT* p = p0; | 
|  | *p++ = fmtch; | 
|  | if (exp < 0) { | 
|  | exp = -exp; | 
|  | *p++ = '-'; | 
|  | } else { | 
|  | *p++ = '+'; | 
|  | } | 
|  |  | 
|  | CharT expbuf[MAXEXPDIG]; | 
|  | CharT* t = expbuf + MAXEXPDIG; | 
|  | if (exp > 9) { | 
|  | do { | 
|  | *--t = to_char(exp % 10); | 
|  | } while ((exp /= 10) > 9); | 
|  | *--t = to_char(exp); | 
|  | for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */; | 
|  | } else { | 
|  | /* | 
|  | * Exponents for decimal floating point conversions | 
|  | * (%[eEgG]) must be at least two characters long, | 
|  | * whereas exponents for hexadecimal conversions can | 
|  | * be only one character long. | 
|  | */ | 
|  | if (fmtch == 'e' || fmtch == 'E') *p++ = '0'; | 
|  | *p++ = to_char(exp); | 
|  | } | 
|  | return (p - p0); | 
|  | } | 
|  |  | 
|  | #define PAD(howmany, with)     \ | 
|  | do {                         \ | 
|  | if ((n = (howmany)) > 0) { \ | 
|  | while (n > PADSIZE) {    \ | 
|  | PRINT(with, PADSIZE);  \ | 
|  | n -= PADSIZE;          \ | 
|  | }                        \ | 
|  | PRINT(with, n);          \ | 
|  | }                          \ | 
|  | } while (0) | 
|  |  | 
|  | #define PRINTANDPAD(p, ep, len, with)       \ | 
|  | do {                                      \ | 
|  | n2 = (ep) - (p);                        \ | 
|  | if (n2 > (len)) n2 = (len);             \ | 
|  | if (n2 > 0) PRINT((p), n2);             \ | 
|  | PAD((len) - (n2 > 0 ? n2 : 0), (with)); \ | 
|  | } while (0) | 
|  |  | 
|  | /* | 
|  | * The size of the buffer we use as scratch space for integer | 
|  | * conversions, among other things.  Technically, we would need the | 
|  | * most space for base 10 conversions with thousands' grouping | 
|  | * characters between each pair of digits.  100 bytes is a | 
|  | * conservative overestimate even for a 128-bit uintmax_t. | 
|  | */ | 
|  | #define BUF 100 | 
|  |  | 
|  | #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ | 
|  |  | 
|  | /* | 
|  | * Flags used during conversion. | 
|  | */ | 
|  | #define ALT 0x0001      /* alternate form */ | 
|  | #define LADJUST 0x0004  /* left adjustment */ | 
|  | #define LONGDBL 0x0008  /* long double */ | 
|  | #define LONGINT 0x0010  /* long integer */ | 
|  | #define LLONGINT 0x0020 /* long long integer */ | 
|  | #define SHORTINT 0x0040 /* short integer */ | 
|  | #define ZEROPAD 0x0080  /* zero (as opposed to blank) pad */ | 
|  | #define FPT 0x0100      /* Floating point number */ | 
|  | #define PTRINT 0x0200   /* (unsigned) ptrdiff_t */ | 
|  | #define SIZEINT 0x0400  /* (signed) size_t */ | 
|  | #define CHARINT 0x0800  /* 8 bit integer */ | 
|  | #define MAXINT 0x1000   /* largest integer size (intmax_t) */ | 
|  |  | 
|  | /* | 
|  | * Type ids for argument type table. | 
|  | */ | 
|  | #define T_UNUSED 0 | 
|  | #define T_SHORT 1 | 
|  | #define T_U_SHORT 2 | 
|  | #define TP_SHORT 3 | 
|  | #define T_INT 4 | 
|  | #define T_U_INT 5 | 
|  | #define TP_INT 6 | 
|  | #define T_LONG 7 | 
|  | #define T_U_LONG 8 | 
|  | #define TP_LONG 9 | 
|  | #define T_LLONG 10 | 
|  | #define T_U_LLONG 11 | 
|  | #define TP_LLONG 12 | 
|  | #define T_DOUBLE 13 | 
|  | #define T_LONG_DOUBLE 14 | 
|  | #define TP_CHAR 15 | 
|  | #define TP_VOID 16 | 
|  | #define T_PTRINT 17 | 
|  | #define TP_PTRINT 18 | 
|  | #define T_SIZEINT 19 | 
|  | #define T_SSIZEINT 20 | 
|  | #define TP_SSIZEINT 21 | 
|  | #define T_MAXINT 22 | 
|  | #define T_MAXUINT 23 | 
|  | #define TP_MAXINT 24 | 
|  | #define T_CHAR 25 | 
|  | #define T_U_CHAR 26 | 
|  | #define T_WINT 27 | 
|  | #define TP_WCHAR 28 | 
|  |  | 
|  | // To extend shorts properly, we need both signed and unsigned | 
|  | // argument extraction methods. | 
|  | #define SARG()                                                                               \ | 
|  | ((intmax_t)(flags & MAXINT                                                                 \ | 
|  | ? GETARG(intmax_t)                                                         \ | 
|  | : flags & LLONGINT                                                         \ | 
|  | ? GETARG(long long)                                                  \ | 
|  | : flags & LONGINT                                                    \ | 
|  | ? GETARG(long)                                                 \ | 
|  | : flags & PTRINT                                               \ | 
|  | ? GETARG(ptrdiff_t)                                      \ | 
|  | : flags & SIZEINT                                        \ | 
|  | ? GETARG(ssize_t)                                  \ | 
|  | : flags & SHORTINT                                 \ | 
|  | ? (short)GETARG(int)                         \ | 
|  | : flags & CHARINT ? (signed char)GETARG(int) \ | 
|  | : GETARG(int))) | 
|  | #define UARG()                                                                                \ | 
|  | ((uintmax_t)(flags & MAXINT                                                                 \ | 
|  | ? GETARG(uintmax_t)                                                        \ | 
|  | : flags & LLONGINT                                                         \ | 
|  | ? GETARG(unsigned long long)                                         \ | 
|  | : flags & LONGINT                                                    \ | 
|  | ? GETARG(unsigned long)                                        \ | 
|  | : flags & PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */    \ | 
|  | flags & SIZEINT                                          \ | 
|  | ? GETARG(size_t)                                     \ | 
|  | : flags & SHORTINT                                   \ | 
|  | ? (unsigned short)GETARG(int)                  \ | 
|  | : flags & CHARINT ? (unsigned char)GETARG(int) \ | 
|  | : GETARG(unsigned int))) | 
|  |  | 
|  | // Append a digit to a value and check for overflow. | 
|  | #define APPEND_DIGIT(val, dig)                            \ | 
|  | do {                                                    \ | 
|  | if ((val) > INT_MAX / 10) goto overflow;              \ | 
|  | (val) *= 10;                                          \ | 
|  | if ((val) > INT_MAX - to_digit((dig))) goto overflow; \ | 
|  | (val) += to_digit((dig));                             \ | 
|  | } while (0) | 
|  |  | 
|  | // Get * arguments, including the form *nn$.  Preserve the nextarg | 
|  | // that the argument can be gotten once the type is determined. | 
|  | #define GETASTER(val)                                                     \ | 
|  | n2 = 0;                                                                 \ | 
|  | cp = fmt;                                                               \ | 
|  | while (is_digit(*cp)) {                                                 \ | 
|  | APPEND_DIGIT(n2, *cp);                                                \ | 
|  | cp++;                                                                 \ | 
|  | }                                                                       \ | 
|  | if (*cp == '$') {                                                       \ | 
|  | int hold = nextarg;                                                   \ | 
|  | if (argtable == NULL) {                                               \ | 
|  | argtable = statargtable;                                            \ | 
|  | if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { \ | 
|  | ret = -1;                                                         \ | 
|  | goto error;                                                       \ | 
|  | }                                                                   \ | 
|  | }                                                                     \ | 
|  | nextarg = n2;                                                         \ | 
|  | val = GETARG(int);                                                    \ | 
|  | nextarg = hold;                                                       \ | 
|  | fmt = ++cp;                                                           \ | 
|  | } else {                                                                \ | 
|  | val = GETARG(int);                                                    \ | 
|  | } | 
|  |  | 
|  | // Get the argument indexed by nextarg.   If the argument table is | 
|  | // built, use it to get the argument.  If its not, get the next | 
|  | // argument (and arguments must be gotten sequentially). | 
|  | #define GETARG(type) \ | 
|  | ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type))) | 
|  |  | 
|  | /* | 
|  | * Find all arguments when a positional parameter is encountered.  Returns a | 
|  | * table, indexed by argument number, of pointers to each arguments.  The | 
|  | * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. | 
|  | * It will be replaced with a mmap-ed one if it overflows (malloc cannot be | 
|  | * used since we are attempting to make snprintf thread safe, and alloca is | 
|  | * problematic since we have nested functions..) | 
|  | */ | 
|  | static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, | 
|  | size_t* argtablesiz) { | 
|  | int ch;                   /* character from fmt */ | 
|  | int n, n2;                /* handy integer (short term usage) */ | 
|  | int flags;                /* flags as above */ | 
|  | unsigned char* typetable; /* table of types */ | 
|  | unsigned char stattypetable[STATIC_ARG_TBL_SIZE]; | 
|  | int tablesize; /* current size of type table */ | 
|  | int tablemax;  /* largest used index in table */ | 
|  | int nextarg;   /* 1-based argument index */ | 
|  | int ret = 0;   /* return value */ | 
|  |  | 
|  | /* | 
|  | * Add an argument type to the table, expanding if necessary. | 
|  | */ | 
|  | #define ADDTYPE(type)                                                      \ | 
|  | ((nextarg >= tablesize) ? __grow_type_table(&typetable, &tablesize) : 0, \ | 
|  | (nextarg > tablemax) ? tablemax = nextarg : 0, typetable[nextarg++] = type) | 
|  |  | 
|  | #define ADDSARG()                                                                             \ | 
|  | ((flags & MAXINT)                                                                           \ | 
|  | ? ADDTYPE(T_MAXINT)                                                                    \ | 
|  | : ((flags & PTRINT) ? ADDTYPE(T_PTRINT)                                                \ | 
|  | : ((flags & SIZEINT)                                               \ | 
|  | ? ADDTYPE(T_SSIZEINT)                                       \ | 
|  | : ((flags & LLONGINT)                                       \ | 
|  | ? ADDTYPE(T_LLONG)                                   \ | 
|  | : ((flags & LONGINT)                                 \ | 
|  | ? ADDTYPE(T_LONG)                             \ | 
|  | : ((flags & SHORTINT)                         \ | 
|  | ? ADDTYPE(T_SHORT)                     \ | 
|  | : ((flags & CHARINT) ? ADDTYPE(T_CHAR) \ | 
|  | : ADDTYPE(T_INT)))))))) | 
|  |  | 
|  | #define ADDUARG()                                                                  \ | 
|  | ((flags & MAXINT)                                                                \ | 
|  | ? ADDTYPE(T_MAXUINT)                                                        \ | 
|  | : ((flags & PTRINT)                                                         \ | 
|  | ? ADDTYPE(T_PTRINT)                                                  \ | 
|  | : ((flags & SIZEINT)                                                 \ | 
|  | ? ADDTYPE(T_SIZEINT)                                          \ | 
|  | : ((flags & LLONGINT)                                         \ | 
|  | ? ADDTYPE(T_U_LLONG)                                   \ | 
|  | : ((flags & LONGINT)                                   \ | 
|  | ? ADDTYPE(T_U_LONG)                             \ | 
|  | : ((flags & SHORTINT)                           \ | 
|  | ? ADDTYPE(T_U_SHORT)                     \ | 
|  | : ((flags & CHARINT) ? ADDTYPE(T_U_CHAR) \ | 
|  | : ADDTYPE(T_U_INT)))))))) | 
|  |  | 
|  | /* | 
|  | * Add * arguments to the type array. | 
|  | */ | 
|  | #define ADDASTER()         \ | 
|  | n2 = 0;                  \ | 
|  | cp = fmt;                \ | 
|  | while (is_digit(*cp)) {  \ | 
|  | APPEND_DIGIT(n2, *cp); \ | 
|  | cp++;                  \ | 
|  | }                        \ | 
|  | if (*cp == '$') {        \ | 
|  | int hold = nextarg;    \ | 
|  | nextarg = n2;          \ | 
|  | ADDTYPE(T_INT);        \ | 
|  | nextarg = hold;        \ | 
|  | fmt = ++cp;            \ | 
|  | } else {                 \ | 
|  | ADDTYPE(T_INT);        \ | 
|  | } | 
|  | CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0); | 
|  | CHAR_TYPE* cp; | 
|  | typetable = stattypetable; | 
|  | tablesize = STATIC_ARG_TBL_SIZE; | 
|  | tablemax = 0; | 
|  | nextarg = 1; | 
|  | memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); | 
|  |  | 
|  | /* | 
|  | * Scan the format for conversions (`%' character). | 
|  | */ | 
|  | for (;;) { | 
|  | for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; | 
|  | if (ch == '\0') goto done; | 
|  | fmt++; /* skip over '%' */ | 
|  |  | 
|  | flags = 0; | 
|  |  | 
|  | rflag: | 
|  | ch = *fmt++; | 
|  | reswitch: | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | case '#': | 
|  | case '\'': | 
|  | goto rflag; | 
|  | case '*': | 
|  | ADDASTER(); | 
|  | goto rflag; | 
|  | case '-': | 
|  | case '+': | 
|  | goto rflag; | 
|  | case '.': | 
|  | if ((ch = *fmt++) == '*') { | 
|  | ADDASTER(); | 
|  | goto rflag; | 
|  | } | 
|  | while (is_digit(ch)) { | 
|  | ch = *fmt++; | 
|  | } | 
|  | goto reswitch; | 
|  | case '0': | 
|  | goto rflag; | 
|  | case '1': | 
|  | case '2': | 
|  | case '3': | 
|  | case '4': | 
|  | case '5': | 
|  | case '6': | 
|  | case '7': | 
|  | case '8': | 
|  | case '9': | 
|  | n = 0; | 
|  | do { | 
|  | APPEND_DIGIT(n, ch); | 
|  | ch = *fmt++; | 
|  | } while (is_digit(ch)); | 
|  | if (ch == '$') { | 
|  | nextarg = n; | 
|  | goto rflag; | 
|  | } | 
|  | goto reswitch; | 
|  | case 'L': | 
|  | flags |= LONGDBL; | 
|  | goto rflag; | 
|  | case 'h': | 
|  | if (*fmt == 'h') { | 
|  | fmt++; | 
|  | flags |= CHARINT; | 
|  | } else { | 
|  | flags |= SHORTINT; | 
|  | } | 
|  | goto rflag; | 
|  | case 'j': | 
|  | flags |= MAXINT; | 
|  | goto rflag; | 
|  | case 'l': | 
|  | if (*fmt == 'l') { | 
|  | fmt++; | 
|  | flags |= LLONGINT; | 
|  | } else { | 
|  | flags |= LONGINT; | 
|  | } | 
|  | goto rflag; | 
|  | case 'q': | 
|  | flags |= LLONGINT; | 
|  | goto rflag; | 
|  | case 't': | 
|  | flags |= PTRINT; | 
|  | goto rflag; | 
|  | case 'z': | 
|  | flags |= SIZEINT; | 
|  | goto rflag; | 
|  | case 'C': | 
|  | flags |= LONGINT; | 
|  | __BIONIC_FALLTHROUGH; | 
|  | case 'c': | 
|  | if (flags & LONGINT) | 
|  | ADDTYPE(T_WINT); | 
|  | else | 
|  | ADDTYPE(T_INT); | 
|  | break; | 
|  | case 'D': | 
|  | flags |= LONGINT; | 
|  | __BIONIC_FALLTHROUGH; | 
|  | case 'd': | 
|  | case 'i': | 
|  | ADDSARG(); | 
|  | break; | 
|  | case 'a': | 
|  | case 'A': | 
|  | case 'e': | 
|  | case 'E': | 
|  | case 'f': | 
|  | case 'F': | 
|  | case 'g': | 
|  | case 'G': | 
|  | if (flags & LONGDBL) | 
|  | ADDTYPE(T_LONG_DOUBLE); | 
|  | else | 
|  | ADDTYPE(T_DOUBLE); | 
|  | break; | 
|  | case 'n': | 
|  | __fortify_fatal("%%n not allowed on Android"); | 
|  | case 'O': | 
|  | flags |= LONGINT; | 
|  | __BIONIC_FALLTHROUGH; | 
|  | case 'o': | 
|  | ADDUARG(); | 
|  | break; | 
|  | case 'p': | 
|  | ADDTYPE(TP_VOID); | 
|  | break; | 
|  | case 'S': | 
|  | flags |= LONGINT; | 
|  | __BIONIC_FALLTHROUGH; | 
|  | case 's': | 
|  | ADDTYPE((flags & LONGINT) ? TP_WCHAR : TP_CHAR); | 
|  | break; | 
|  | case 'U': | 
|  | flags |= LONGINT; | 
|  | __BIONIC_FALLTHROUGH; | 
|  | case 'u': | 
|  | case 'X': | 
|  | case 'x': | 
|  | case 'B': | 
|  | case 'b': | 
|  | ADDUARG(); | 
|  | break; | 
|  | case 'w': { | 
|  | n = 0; | 
|  | bool fast = false; | 
|  | ch = *fmt++; | 
|  | if (ch == 'f') { | 
|  | fast = true; | 
|  | ch = *fmt++; | 
|  | } | 
|  | while (is_digit(ch)) { | 
|  | APPEND_DIGIT(n, ch); | 
|  | ch = *fmt++; | 
|  | } | 
|  | if (n == 64) { | 
|  | flags |= LLONGINT; | 
|  | } else { | 
|  | if (n != 8 && fast) { | 
|  | #if defined(__LP64__) | 
|  | flags |= LLONGINT; | 
|  | #endif | 
|  | } | 
|  | } | 
|  | goto reswitch; | 
|  | } | 
|  | default: /* "%?" prints ?, unless ? is NUL */ | 
|  | if (ch == '\0') goto done; | 
|  | break; | 
|  | } | 
|  | } | 
|  | done: | 
|  | /* | 
|  | * Build the argument table. | 
|  | */ | 
|  | if (tablemax >= STATIC_ARG_TBL_SIZE) { | 
|  | *argtablesiz = sizeof(union arg) * (tablemax + 1); | 
|  | *argtable = static_cast<arg*>(mmap(nullptr, *argtablesiz, | 
|  | PROT_WRITE | PROT_READ, | 
|  | MAP_ANON | MAP_PRIVATE, -1, 0)); | 
|  | if (*argtable == MAP_FAILED) return -1; | 
|  | } | 
|  |  | 
|  | for (n = 1; n <= tablemax; n++) { | 
|  | switch (typetable[n]) { | 
|  | case T_UNUSED: | 
|  | case T_CHAR: | 
|  | case T_U_CHAR: | 
|  | case T_SHORT: | 
|  | case T_U_SHORT: | 
|  | case T_INT: | 
|  | (*argtable)[n].intarg = va_arg(ap, int); | 
|  | break; | 
|  | case TP_SHORT: | 
|  | (*argtable)[n].pshortarg = va_arg(ap, short*); | 
|  | break; | 
|  | case T_U_INT: | 
|  | (*argtable)[n].uintarg = va_arg(ap, unsigned int); | 
|  | break; | 
|  | case TP_INT: | 
|  | (*argtable)[n].pintarg = va_arg(ap, int*); | 
|  | break; | 
|  | case T_LONG: | 
|  | (*argtable)[n].longarg = va_arg(ap, long); | 
|  | break; | 
|  | case T_U_LONG: | 
|  | (*argtable)[n].ulongarg = va_arg(ap, unsigned long); | 
|  | break; | 
|  | case TP_LONG: | 
|  | (*argtable)[n].plongarg = va_arg(ap, long*); | 
|  | break; | 
|  | case T_LLONG: | 
|  | (*argtable)[n].longlongarg = va_arg(ap, long long); | 
|  | break; | 
|  | case T_U_LLONG: | 
|  | (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long); | 
|  | break; | 
|  | case TP_LLONG: | 
|  | (*argtable)[n].plonglongarg = va_arg(ap, long long*); | 
|  | break; | 
|  | case T_DOUBLE: | 
|  | (*argtable)[n].doublearg = va_arg(ap, double); | 
|  | break; | 
|  | case T_LONG_DOUBLE: | 
|  | (*argtable)[n].longdoublearg = va_arg(ap, long double); | 
|  | break; | 
|  | case TP_CHAR: | 
|  | (*argtable)[n].pchararg = va_arg(ap, char*); | 
|  | break; | 
|  | case TP_VOID: | 
|  | (*argtable)[n].pvoidarg = va_arg(ap, void*); | 
|  | break; | 
|  | case T_PTRINT: | 
|  | (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t); | 
|  | break; | 
|  | case TP_PTRINT: | 
|  | (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*); | 
|  | break; | 
|  | case T_SIZEINT: | 
|  | (*argtable)[n].sizearg = va_arg(ap, size_t); | 
|  | break; | 
|  | case T_SSIZEINT: | 
|  | (*argtable)[n].ssizearg = va_arg(ap, ssize_t); | 
|  | break; | 
|  | case TP_SSIZEINT: | 
|  | (*argtable)[n].pssizearg = va_arg(ap, ssize_t*); | 
|  | break; | 
|  | case T_MAXINT: | 
|  | (*argtable)[n].intmaxarg = va_arg(ap, intmax_t); | 
|  | break; | 
|  | case T_MAXUINT: | 
|  | (*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t); | 
|  | break; | 
|  | case TP_MAXINT: | 
|  | (*argtable)[n].pintmaxarg = va_arg(ap, intmax_t*); | 
|  | break; | 
|  | case T_WINT: | 
|  | (*argtable)[n].wintarg = va_arg(ap, wint_t); | 
|  | break; | 
|  | case TP_WCHAR: | 
|  | (*argtable)[n].pwchararg = va_arg(ap, wchar_t*); | 
|  | break; | 
|  | } | 
|  | } | 
|  | goto finish; | 
|  |  | 
|  | overflow: | 
|  | errno = ENOMEM; | 
|  | ret = -1; | 
|  |  | 
|  | finish: | 
|  | if (typetable != nullptr && typetable != stattypetable) { | 
|  | munmap(typetable, *argtablesiz); | 
|  | typetable = nullptr; | 
|  | } | 
|  | return (ret); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Increase the size of the type table. | 
|  | */ | 
|  | static int __grow_type_table(unsigned char** typetable, int* tablesize) { | 
|  | unsigned char* old_table = *typetable; | 
|  | int new_size = *tablesize * 2; | 
|  |  | 
|  | if (new_size < getpagesize()) new_size = getpagesize(); | 
|  |  | 
|  | if (*tablesize == STATIC_ARG_TBL_SIZE) { | 
|  | *typetable = static_cast<unsigned char*>(mmap(nullptr, new_size, | 
|  | PROT_WRITE | PROT_READ, | 
|  | MAP_ANON | MAP_PRIVATE, -1, 0)); | 
|  | if (*typetable == MAP_FAILED) return -1; | 
|  | bcopy(old_table, *typetable, *tablesize); | 
|  | } else { | 
|  | unsigned char* new_table = static_cast<unsigned char*>(mmap(nullptr, new_size, | 
|  | PROT_WRITE | PROT_READ, | 
|  | MAP_ANON | MAP_PRIVATE, -1, 0)); | 
|  | if (new_table == MAP_FAILED) return -1; | 
|  | memmove(new_table, *typetable, *tablesize); | 
|  | munmap(*typetable, *tablesize); | 
|  | *typetable = new_table; | 
|  | } | 
|  | memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize)); | 
|  |  | 
|  | *tablesize = new_size; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | struct helpers { | 
|  | // Flush out all the vectors defined by the given uio, | 
|  | // then reset it so that it can be reused. | 
|  | static int sprint(FILE* fp, struct __suio* uio) { | 
|  | if (uio->uio_resid == 0) { | 
|  | uio->uio_iovcnt = 0; | 
|  | return 0; | 
|  | } | 
|  | int result = __sfvwrite(fp, uio); | 
|  | uio->uio_resid = 0; | 
|  | uio->uio_iovcnt = 0; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // Convert a wide character string argument for the %ls format to a multibyte | 
|  | // string representation. If not -1, prec specifies the maximum number of | 
|  | // bytes to output, and also means that we can't assume that the wide char | 
|  | // string is null-terminated. | 
|  | static char* wcsconv(wchar_t* wcsarg, int prec) { | 
|  | mbstate_t mbs; | 
|  | char buf[MB_LEN_MAX]; | 
|  | wchar_t* p; | 
|  | char* convbuf; | 
|  | size_t clen, nbytes; | 
|  |  | 
|  | // Allocate space for the maximum number of bytes we could output. | 
|  | if (prec < 0) { | 
|  | memset(&mbs, 0, sizeof(mbs)); | 
|  | p = wcsarg; | 
|  | nbytes = wcsrtombs(nullptr, (const wchar_t**)&p, 0, &mbs); | 
|  | if (nbytes == (size_t)-1) return nullptr; | 
|  | } else { | 
|  | // Optimisation: if the output precision is small enough, | 
|  | // just allocate enough memory for the maximum instead of | 
|  | // scanning the string. | 
|  | if (prec < 128) { | 
|  | nbytes = prec; | 
|  | } else { | 
|  | nbytes = 0; | 
|  | p = wcsarg; | 
|  | memset(&mbs, 0, sizeof(mbs)); | 
|  | for (;;) { | 
|  | clen = wcrtomb(buf, *p++, &mbs); | 
|  | if (clen == 0 || clen == (size_t)-1 || nbytes + clen > (size_t)prec) break; | 
|  | nbytes += clen; | 
|  | } | 
|  | if (clen == (size_t)-1) return nullptr; | 
|  | } | 
|  | } | 
|  | if ((convbuf = static_cast<char*>(malloc(nbytes + 1))) == nullptr) return nullptr; | 
|  |  | 
|  | // Fill the output buffer. | 
|  | p = wcsarg; | 
|  | memset(&mbs, 0, sizeof(mbs)); | 
|  | if ((nbytes = wcsrtombs(convbuf, (const wchar_t**)&p, nbytes, &mbs)) == (size_t)-1) { | 
|  | free(convbuf); | 
|  | return nullptr; | 
|  | } | 
|  | convbuf[nbytes] = '\0'; | 
|  | return convbuf; | 
|  | } | 
|  |  | 
|  | // Like __fputwc_unlock, but handles fake string (__SSTR) files properly. | 
|  | // File must already be locked. | 
|  | static wint_t xfputwc(wchar_t wc, FILE* fp) { | 
|  | if ((fp->_flags & __SSTR) == 0) return __fputwc_unlock(wc, fp); | 
|  |  | 
|  | char buf[MB_LEN_MAX]; | 
|  | mbstate_t mbs = {}; | 
|  | size_t len = wcrtomb(buf, wc, &mbs); | 
|  | if (len == (size_t)-1) { | 
|  | fp->_flags |= __SERR; | 
|  | errno = EILSEQ; | 
|  | return WEOF; | 
|  | } | 
|  |  | 
|  | struct __siov iov; | 
|  | iov.iov_base = buf; | 
|  | iov.iov_len = len; | 
|  | struct __suio uio; | 
|  | uio.uio_iov = &iov; | 
|  | uio.uio_resid = len; | 
|  | uio.uio_iovcnt = 1; | 
|  | return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF); | 
|  | } | 
|  |  | 
|  | // Convert a multibyte character string argument for the %s format to a wide | 
|  | // string representation. ``prec'' specifies the maximum number of bytes | 
|  | // to output. If ``prec'' is greater than or equal to zero, we can't assume | 
|  | // that the multibyte character string ends in a null character. | 
|  | // | 
|  | // Returns NULL on failure. | 
|  | // To find out what happened check errno for ENOMEM, EILSEQ and EINVAL. | 
|  | static wchar_t* mbsconv(char* mbsarg, int prec) { | 
|  | mbstate_t mbs; | 
|  | const char* p; | 
|  | size_t insize, nchars, nconv; | 
|  |  | 
|  | if (mbsarg == nullptr) return nullptr; | 
|  |  | 
|  | // Supplied argument is a multibyte string; convert it to wide characters first. | 
|  | if (prec >= 0) { | 
|  | // String is not guaranteed to be NUL-terminated. Find the number of characters to print. | 
|  | p = mbsarg; | 
|  | insize = nchars = nconv = 0; | 
|  | bzero(&mbs, sizeof(mbs)); | 
|  | while (nchars != (size_t)prec) { | 
|  | nconv = mbrlen(p, MB_CUR_MAX, &mbs); | 
|  | if (nconv == (size_t)0 || nconv == (size_t)-1 || nconv == (size_t)-2) break; | 
|  | p += nconv; | 
|  | nchars++; | 
|  | insize += nconv; | 
|  | } | 
|  | if (nconv == (size_t)-1 || nconv == (size_t)-2) return (nullptr); | 
|  | } else { | 
|  | insize = strlen(mbsarg); | 
|  | } | 
|  |  | 
|  | // Allocate buffer for the result and perform the conversion, | 
|  | // converting at most `size' bytes of the input multibyte string to | 
|  | // wide characters for printing. | 
|  | wchar_t* convbuf = static_cast<wchar_t*>(calloc(insize + 1, sizeof(*convbuf))); | 
|  | if (convbuf == nullptr) return nullptr; | 
|  | wchar_t* wcp = convbuf; | 
|  | p = mbsarg; | 
|  | bzero(&mbs, sizeof(mbs)); | 
|  | nconv = 0; | 
|  | while (insize != 0) { | 
|  | nconv = mbrtowc(wcp, p, insize, &mbs); | 
|  | if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) break; | 
|  | wcp++; | 
|  | p += nconv; | 
|  | insize -= nconv; | 
|  | } | 
|  | if (nconv == (size_t)-1 || nconv == (size_t)-2) { | 
|  | free(convbuf); | 
|  | return nullptr; | 
|  | } | 
|  | *wcp = '\0'; | 
|  |  | 
|  | return convbuf; | 
|  | } | 
|  |  | 
|  | // Trasnlate a fixed size integer argument for the %w/%wf format to a | 
|  | // flag representation. Supported sizes are 8, 16, 32, and 64 so far. | 
|  | // See details in bionic/libc/include/stdint.h | 
|  | static int w_to_flag(int size, bool fast) { | 
|  | static constexpr int fast_size = sizeof(void*) == 8 ? LLONGINT : 0; | 
|  | if (size == 8) return CHARINT; | 
|  | if (size == 16) return fast ? fast_size : SHORTINT; | 
|  | if (size == 32) return fast ? fast_size : 0; | 
|  | if (size == 64) return LLONGINT; | 
|  | __fortify_fatal("%%w%s%d is unsupported", fast ? "f" : "", size); | 
|  | } | 
|  | }; |