815 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			815 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*-
 | |
|  * 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':
 | |
|         ADDUARG();
 | |
|         break;
 | |
|       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;
 | |
|   }
 | |
| 
 | |
| };
 |