| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 1 | /*- | 
|  | 2 | * Copyright (c) 1990 The Regents of the University of California. | 
|  | 3 | * All rights reserved. | 
|  | 4 | * | 
|  | 5 | * This code is derived from software contributed to Berkeley by | 
|  | 6 | * Chris Torek. | 
|  | 7 | * | 
|  | 8 | * Redistribution and use in source and binary forms, with or without | 
|  | 9 | * modification, are permitted provided that the following conditions | 
|  | 10 | * are met: | 
|  | 11 | * 1. Redistributions of source code must retain the above copyright | 
|  | 12 | *    notice, this list of conditions and the following disclaimer. | 
|  | 13 | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | 14 | *    notice, this list of conditions and the following disclaimer in the | 
|  | 15 | *    documentation and/or other materials provided with the distribution. | 
|  | 16 | * 3. Neither the name of the University nor the names of its contributors | 
|  | 17 | *    may be used to endorse or promote products derived from this software | 
|  | 18 | *    without specific prior written permission. | 
|  | 19 | * | 
|  | 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
|  | 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | 23 | * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
|  | 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | 30 | * SUCH DAMAGE. | 
|  | 31 | */ | 
|  | 32 |  | 
|  | 33 | #include <sys/mman.h> | 
|  | 34 | #include <sys/types.h> | 
|  | 35 |  | 
|  | 36 | #include <errno.h> | 
|  | 37 | #include <float.h> | 
|  | 38 | #include <langinfo.h> | 
|  | 39 | #include <limits.h> | 
|  | 40 | #include <locale.h> | 
|  | 41 | #include <math.h> | 
|  | 42 | #include <stdarg.h> | 
|  | 43 | #include <stddef.h> | 
|  | 44 | #include <stdint.h> | 
|  | 45 | #include <stdio.h> | 
|  | 46 | #include <stdlib.h> | 
|  | 47 | #include <string.h> | 
|  | 48 | #include <unistd.h> | 
|  | 49 | #include <wchar.h> | 
|  | 50 |  | 
| Josh Gao | 4956c37 | 2019-12-19 16:35:51 -0800 | [diff] [blame] | 51 | #include <platform/bionic/macros.h> | 
| George Burgess IV | fa5410f | 2018-08-13 17:44:06 -0700 | [diff] [blame] | 52 |  | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 53 | #include "fvwrite.h" | 
|  | 54 | #include "gdtoa.h" | 
|  | 55 | #include "local.h" | 
|  | 56 |  | 
|  | 57 | union arg { | 
|  | 58 | int intarg; | 
|  | 59 | unsigned int uintarg; | 
|  | 60 | long longarg; | 
|  | 61 | unsigned long ulongarg; | 
|  | 62 | long long longlongarg; | 
|  | 63 | unsigned long long ulonglongarg; | 
|  | 64 | ptrdiff_t ptrdiffarg; | 
|  | 65 | size_t sizearg; | 
|  | 66 | ssize_t ssizearg; | 
|  | 67 | intmax_t intmaxarg; | 
|  | 68 | uintmax_t uintmaxarg; | 
|  | 69 | void* pvoidarg; | 
|  | 70 | char* pchararg; | 
|  | 71 | signed char* pschararg; | 
|  | 72 | short* pshortarg; | 
|  | 73 | int* pintarg; | 
|  | 74 | long* plongarg; | 
|  | 75 | long long* plonglongarg; | 
|  | 76 | ptrdiff_t* pptrdiffarg; | 
|  | 77 | ssize_t* pssizearg; | 
|  | 78 | intmax_t* pintmaxarg; | 
|  | 79 | double doublearg; | 
|  | 80 | long double longdoublearg; | 
|  | 81 | wint_t wintarg; | 
|  | 82 | wchar_t* pwchararg; | 
|  | 83 | }; | 
|  | 84 |  | 
|  | 85 | // Helper function for `fprintf to unbuffered unix file': creates a | 
|  | 86 | // temporary buffer.  We only work on write-only files; this avoids | 
|  | 87 | // worries about ungetc buffers and so forth. | 
|  | 88 | static int __sbprintf(FILE* fp, const CHAR_TYPE* fmt, va_list ap) { | 
|  | 89 | FILE fake; | 
|  | 90 | struct __sfileext fakeext; | 
|  | 91 | unsigned char buf[BUFSIZ]; | 
|  | 92 |  | 
|  | 93 | _FILEEXT_SETUP(&fake, &fakeext); | 
|  | 94 | /* copy the important variables */ | 
|  | 95 | fake._flags = fp->_flags & ~__SNBF; | 
|  | 96 | fake._file = fp->_file; | 
|  | 97 | fake._cookie = fp->_cookie; | 
|  | 98 | fake._write = fp->_write; | 
|  | 99 |  | 
|  | 100 | /* set up the buffer */ | 
|  | 101 | fake._bf._base = fake._p = buf; | 
|  | 102 | fake._bf._size = fake._w = sizeof(buf); | 
|  | 103 | fake._lbfsize = 0; /* not actually used, but Just In Case */ | 
|  | 104 |  | 
|  | 105 | /* do the work, then copy any error status */ | 
|  | 106 | int ret = FUNCTION_NAME(&fake, fmt, ap); | 
|  | 107 | if (ret >= 0 && __sflush(&fake)) ret = EOF; | 
|  | 108 | if (fake._flags & __SERR) fp->_flags |= __SERR; | 
|  | 109 | return ret; | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, size_t* argtablesiz); | 
|  | 113 | static int __grow_type_table(unsigned char** typetable, int* tablesize); | 
|  | 114 |  | 
|  | 115 | #define DEFPREC 6 | 
|  | 116 |  | 
|  | 117 | #define to_digit(c) ((c) - '0') | 
|  | 118 | #define is_digit(c) ((unsigned)to_digit(c) <= 9) | 
|  | 119 | #define to_char(n) ((CHAR_TYPE)((n) + '0')) | 
|  | 120 |  | 
|  | 121 | template <typename CharT> | 
|  | 122 | static int exponent(CharT* p0, int exp, int fmtch) { | 
|  | 123 | CharT* p = p0; | 
|  | 124 | *p++ = fmtch; | 
|  | 125 | if (exp < 0) { | 
|  | 126 | exp = -exp; | 
|  | 127 | *p++ = '-'; | 
|  | 128 | } else { | 
|  | 129 | *p++ = '+'; | 
|  | 130 | } | 
|  | 131 |  | 
|  | 132 | CharT expbuf[MAXEXPDIG]; | 
|  | 133 | CharT* t = expbuf + MAXEXPDIG; | 
|  | 134 | if (exp > 9) { | 
|  | 135 | do { | 
|  | 136 | *--t = to_char(exp % 10); | 
|  | 137 | } while ((exp /= 10) > 9); | 
|  | 138 | *--t = to_char(exp); | 
|  | 139 | for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */; | 
|  | 140 | } else { | 
|  | 141 | /* | 
|  | 142 | * Exponents for decimal floating point conversions | 
|  | 143 | * (%[eEgG]) must be at least two characters long, | 
|  | 144 | * whereas exponents for hexadecimal conversions can | 
|  | 145 | * be only one character long. | 
|  | 146 | */ | 
|  | 147 | if (fmtch == 'e' || fmtch == 'E') *p++ = '0'; | 
|  | 148 | *p++ = to_char(exp); | 
|  | 149 | } | 
|  | 150 | return (p - p0); | 
|  | 151 | } | 
|  | 152 |  | 
|  | 153 | #define PAD(howmany, with)     \ | 
|  | 154 | do {                         \ | 
|  | 155 | if ((n = (howmany)) > 0) { \ | 
|  | 156 | while (n > PADSIZE) {    \ | 
|  | 157 | PRINT(with, PADSIZE);  \ | 
|  | 158 | n -= PADSIZE;          \ | 
|  | 159 | }                        \ | 
|  | 160 | PRINT(with, n);          \ | 
|  | 161 | }                          \ | 
|  | 162 | } while (0) | 
|  | 163 |  | 
|  | 164 | #define PRINTANDPAD(p, ep, len, with)       \ | 
|  | 165 | do {                                      \ | 
|  | 166 | n2 = (ep) - (p);                        \ | 
|  | 167 | if (n2 > (len)) n2 = (len);             \ | 
|  | 168 | if (n2 > 0) PRINT((p), n2);             \ | 
|  | 169 | PAD((len) - (n2 > 0 ? n2 : 0), (with)); \ | 
|  | 170 | } while (0) | 
|  | 171 |  | 
|  | 172 | /* | 
|  | 173 | * The size of the buffer we use as scratch space for integer | 
|  | 174 | * conversions, among other things.  Technically, we would need the | 
|  | 175 | * most space for base 10 conversions with thousands' grouping | 
|  | 176 | * characters between each pair of digits.  100 bytes is a | 
|  | 177 | * conservative overestimate even for a 128-bit uintmax_t. | 
|  | 178 | */ | 
|  | 179 | #define BUF 100 | 
|  | 180 |  | 
|  | 181 | #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ | 
|  | 182 |  | 
|  | 183 | /* | 
|  | 184 | * Flags used during conversion. | 
|  | 185 | */ | 
|  | 186 | #define ALT 0x0001      /* alternate form */ | 
|  | 187 | #define LADJUST 0x0004  /* left adjustment */ | 
|  | 188 | #define LONGDBL 0x0008  /* long double */ | 
|  | 189 | #define LONGINT 0x0010  /* long integer */ | 
|  | 190 | #define LLONGINT 0x0020 /* long long integer */ | 
|  | 191 | #define SHORTINT 0x0040 /* short integer */ | 
|  | 192 | #define ZEROPAD 0x0080  /* zero (as opposed to blank) pad */ | 
|  | 193 | #define FPT 0x0100      /* Floating point number */ | 
|  | 194 | #define PTRINT 0x0200   /* (unsigned) ptrdiff_t */ | 
|  | 195 | #define SIZEINT 0x0400  /* (signed) size_t */ | 
|  | 196 | #define CHARINT 0x0800  /* 8 bit integer */ | 
|  | 197 | #define MAXINT 0x1000   /* largest integer size (intmax_t) */ | 
|  | 198 |  | 
|  | 199 | /* | 
|  | 200 | * Type ids for argument type table. | 
|  | 201 | */ | 
|  | 202 | #define T_UNUSED 0 | 
|  | 203 | #define T_SHORT 1 | 
|  | 204 | #define T_U_SHORT 2 | 
|  | 205 | #define TP_SHORT 3 | 
|  | 206 | #define T_INT 4 | 
|  | 207 | #define T_U_INT 5 | 
|  | 208 | #define TP_INT 6 | 
|  | 209 | #define T_LONG 7 | 
|  | 210 | #define T_U_LONG 8 | 
|  | 211 | #define TP_LONG 9 | 
|  | 212 | #define T_LLONG 10 | 
|  | 213 | #define T_U_LLONG 11 | 
|  | 214 | #define TP_LLONG 12 | 
|  | 215 | #define T_DOUBLE 13 | 
|  | 216 | #define T_LONG_DOUBLE 14 | 
|  | 217 | #define TP_CHAR 15 | 
|  | 218 | #define TP_VOID 16 | 
|  | 219 | #define T_PTRINT 17 | 
|  | 220 | #define TP_PTRINT 18 | 
|  | 221 | #define T_SIZEINT 19 | 
|  | 222 | #define T_SSIZEINT 20 | 
|  | 223 | #define TP_SSIZEINT 21 | 
|  | 224 | #define T_MAXINT 22 | 
|  | 225 | #define T_MAXUINT 23 | 
|  | 226 | #define TP_MAXINT 24 | 
|  | 227 | #define T_CHAR 25 | 
|  | 228 | #define T_U_CHAR 26 | 
|  | 229 | #define T_WINT 27 | 
|  | 230 | #define TP_WCHAR 28 | 
|  | 231 |  | 
|  | 232 | // To extend shorts properly, we need both signed and unsigned | 
|  | 233 | // argument extraction methods. | 
|  | 234 | #define SARG()                                                                               \ | 
|  | 235 | ((intmax_t)(flags & MAXINT                                                                 \ | 
|  | 236 | ? GETARG(intmax_t)                                                         \ | 
|  | 237 | : flags & LLONGINT                                                         \ | 
|  | 238 | ? GETARG(long long)                                                  \ | 
|  | 239 | : flags & LONGINT                                                    \ | 
|  | 240 | ? GETARG(long)                                                 \ | 
|  | 241 | : flags & PTRINT                                               \ | 
|  | 242 | ? GETARG(ptrdiff_t)                                      \ | 
|  | 243 | : flags & SIZEINT                                        \ | 
|  | 244 | ? GETARG(ssize_t)                                  \ | 
|  | 245 | : flags & SHORTINT                                 \ | 
|  | 246 | ? (short)GETARG(int)                         \ | 
|  | 247 | : flags & CHARINT ? (signed char)GETARG(int) \ | 
|  | 248 | : GETARG(int))) | 
|  | 249 | #define UARG()                                                                                \ | 
|  | 250 | ((uintmax_t)(flags & MAXINT                                                                 \ | 
|  | 251 | ? GETARG(uintmax_t)                                                        \ | 
|  | 252 | : flags & LLONGINT                                                         \ | 
|  | 253 | ? GETARG(unsigned long long)                                         \ | 
|  | 254 | : flags & LONGINT                                                    \ | 
|  | 255 | ? GETARG(unsigned long)                                        \ | 
|  | 256 | : flags & PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */    \ | 
|  | 257 | flags & SIZEINT                                          \ | 
|  | 258 | ? GETARG(size_t)                                     \ | 
|  | 259 | : flags & SHORTINT                                   \ | 
|  | 260 | ? (unsigned short)GETARG(int)                  \ | 
|  | 261 | : flags & CHARINT ? (unsigned char)GETARG(int) \ | 
|  | 262 | : GETARG(unsigned int))) | 
|  | 263 |  | 
|  | 264 | // Append a digit to a value and check for overflow. | 
|  | 265 | #define APPEND_DIGIT(val, dig)                            \ | 
|  | 266 | do {                                                    \ | 
|  | 267 | if ((val) > INT_MAX / 10) goto overflow;              \ | 
|  | 268 | (val) *= 10;                                          \ | 
|  | 269 | if ((val) > INT_MAX - to_digit((dig))) goto overflow; \ | 
|  | 270 | (val) += to_digit((dig));                             \ | 
|  | 271 | } while (0) | 
|  | 272 |  | 
|  | 273 | // Get * arguments, including the form *nn$.  Preserve the nextarg | 
|  | 274 | // that the argument can be gotten once the type is determined. | 
|  | 275 | #define GETASTER(val)                                                     \ | 
|  | 276 | n2 = 0;                                                                 \ | 
|  | 277 | cp = fmt;                                                               \ | 
|  | 278 | while (is_digit(*cp)) {                                                 \ | 
|  | 279 | APPEND_DIGIT(n2, *cp);                                                \ | 
|  | 280 | cp++;                                                                 \ | 
|  | 281 | }                                                                       \ | 
|  | 282 | if (*cp == '$') {                                                       \ | 
|  | 283 | int hold = nextarg;                                                   \ | 
|  | 284 | if (argtable == NULL) {                                               \ | 
|  | 285 | argtable = statargtable;                                            \ | 
|  | 286 | if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { \ | 
|  | 287 | ret = -1;                                                         \ | 
|  | 288 | goto error;                                                       \ | 
|  | 289 | }                                                                   \ | 
|  | 290 | }                                                                     \ | 
|  | 291 | nextarg = n2;                                                         \ | 
|  | 292 | val = GETARG(int);                                                    \ | 
|  | 293 | nextarg = hold;                                                       \ | 
|  | 294 | fmt = ++cp;                                                           \ | 
|  | 295 | } else {                                                                \ | 
|  | 296 | val = GETARG(int);                                                    \ | 
|  | 297 | } | 
|  | 298 |  | 
|  | 299 | // Get the argument indexed by nextarg.   If the argument table is | 
|  | 300 | // built, use it to get the argument.  If its not, get the next | 
|  | 301 | // argument (and arguments must be gotten sequentially). | 
|  | 302 | #define GETARG(type) \ | 
|  | 303 | ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type))) | 
|  | 304 |  | 
|  | 305 | /* | 
|  | 306 | * Find all arguments when a positional parameter is encountered.  Returns a | 
|  | 307 | * table, indexed by argument number, of pointers to each arguments.  The | 
|  | 308 | * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. | 
|  | 309 | * It will be replaced with a mmap-ed one if it overflows (malloc cannot be | 
|  | 310 | * used since we are attempting to make snprintf thread safe, and alloca is | 
|  | 311 | * problematic since we have nested functions..) | 
|  | 312 | */ | 
|  | 313 | static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, | 
|  | 314 | size_t* argtablesiz) { | 
|  | 315 | int ch;                   /* character from fmt */ | 
|  | 316 | int n, n2;                /* handy integer (short term usage) */ | 
|  | 317 | int flags;                /* flags as above */ | 
|  | 318 | unsigned char* typetable; /* table of types */ | 
|  | 319 | unsigned char stattypetable[STATIC_ARG_TBL_SIZE]; | 
|  | 320 | int tablesize; /* current size of type table */ | 
|  | 321 | int tablemax;  /* largest used index in table */ | 
|  | 322 | int nextarg;   /* 1-based argument index */ | 
|  | 323 | int ret = 0;   /* return value */ | 
|  | 324 |  | 
|  | 325 | /* | 
|  | 326 | * Add an argument type to the table, expanding if necessary. | 
|  | 327 | */ | 
|  | 328 | #define ADDTYPE(type)                                                      \ | 
|  | 329 | ((nextarg >= tablesize) ? __grow_type_table(&typetable, &tablesize) : 0, \ | 
|  | 330 | (nextarg > tablemax) ? tablemax = nextarg : 0, typetable[nextarg++] = type) | 
|  | 331 |  | 
|  | 332 | #define ADDSARG()                                                                             \ | 
|  | 333 | ((flags & MAXINT)                                                                           \ | 
|  | 334 | ? ADDTYPE(T_MAXINT)                                                                    \ | 
|  | 335 | : ((flags & PTRINT) ? ADDTYPE(T_PTRINT)                                                \ | 
|  | 336 | : ((flags & SIZEINT)                                               \ | 
|  | 337 | ? ADDTYPE(T_SSIZEINT)                                       \ | 
|  | 338 | : ((flags & LLONGINT)                                       \ | 
|  | 339 | ? ADDTYPE(T_LLONG)                                   \ | 
|  | 340 | : ((flags & LONGINT)                                 \ | 
|  | 341 | ? ADDTYPE(T_LONG)                             \ | 
|  | 342 | : ((flags & SHORTINT)                         \ | 
|  | 343 | ? ADDTYPE(T_SHORT)                     \ | 
|  | 344 | : ((flags & CHARINT) ? ADDTYPE(T_CHAR) \ | 
|  | 345 | : ADDTYPE(T_INT)))))))) | 
|  | 346 |  | 
|  | 347 | #define ADDUARG()                                                                  \ | 
|  | 348 | ((flags & MAXINT)                                                                \ | 
|  | 349 | ? ADDTYPE(T_MAXUINT)                                                        \ | 
|  | 350 | : ((flags & PTRINT)                                                         \ | 
|  | 351 | ? ADDTYPE(T_PTRINT)                                                  \ | 
|  | 352 | : ((flags & SIZEINT)                                                 \ | 
|  | 353 | ? ADDTYPE(T_SIZEINT)                                          \ | 
|  | 354 | : ((flags & LLONGINT)                                         \ | 
|  | 355 | ? ADDTYPE(T_U_LLONG)                                   \ | 
|  | 356 | : ((flags & LONGINT)                                   \ | 
|  | 357 | ? ADDTYPE(T_U_LONG)                             \ | 
|  | 358 | : ((flags & SHORTINT)                           \ | 
|  | 359 | ? ADDTYPE(T_U_SHORT)                     \ | 
|  | 360 | : ((flags & CHARINT) ? ADDTYPE(T_U_CHAR) \ | 
|  | 361 | : ADDTYPE(T_U_INT)))))))) | 
|  | 362 |  | 
|  | 363 | /* | 
|  | 364 | * Add * arguments to the type array. | 
|  | 365 | */ | 
|  | 366 | #define ADDASTER()         \ | 
|  | 367 | n2 = 0;                  \ | 
|  | 368 | cp = fmt;                \ | 
|  | 369 | while (is_digit(*cp)) {  \ | 
|  | 370 | APPEND_DIGIT(n2, *cp); \ | 
|  | 371 | cp++;                  \ | 
|  | 372 | }                        \ | 
|  | 373 | if (*cp == '$') {        \ | 
|  | 374 | int hold = nextarg;    \ | 
|  | 375 | nextarg = n2;          \ | 
|  | 376 | ADDTYPE(T_INT);        \ | 
|  | 377 | nextarg = hold;        \ | 
|  | 378 | fmt = ++cp;            \ | 
|  | 379 | } else {                 \ | 
|  | 380 | ADDTYPE(T_INT);        \ | 
|  | 381 | } | 
|  | 382 | CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0); | 
|  | 383 | CHAR_TYPE* cp; | 
|  | 384 | typetable = stattypetable; | 
|  | 385 | tablesize = STATIC_ARG_TBL_SIZE; | 
|  | 386 | tablemax = 0; | 
|  | 387 | nextarg = 1; | 
|  | 388 | memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); | 
|  | 389 |  | 
|  | 390 | /* | 
|  | 391 | * Scan the format for conversions (`%' character). | 
|  | 392 | */ | 
|  | 393 | for (;;) { | 
|  | 394 | for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; | 
|  | 395 | if (ch == '\0') goto done; | 
|  | 396 | fmt++; /* skip over '%' */ | 
|  | 397 |  | 
|  | 398 | flags = 0; | 
|  | 399 |  | 
|  | 400 | rflag: | 
|  | 401 | ch = *fmt++; | 
|  | 402 | reswitch: | 
|  | 403 | switch (ch) { | 
|  | 404 | case ' ': | 
|  | 405 | case '#': | 
|  | 406 | case '\'': | 
|  | 407 | goto rflag; | 
|  | 408 | case '*': | 
|  | 409 | ADDASTER(); | 
|  | 410 | goto rflag; | 
|  | 411 | case '-': | 
|  | 412 | case '+': | 
|  | 413 | goto rflag; | 
|  | 414 | case '.': | 
|  | 415 | if ((ch = *fmt++) == '*') { | 
|  | 416 | ADDASTER(); | 
|  | 417 | goto rflag; | 
|  | 418 | } | 
|  | 419 | while (is_digit(ch)) { | 
|  | 420 | ch = *fmt++; | 
|  | 421 | } | 
|  | 422 | goto reswitch; | 
|  | 423 | case '0': | 
|  | 424 | goto rflag; | 
|  | 425 | case '1': | 
|  | 426 | case '2': | 
|  | 427 | case '3': | 
|  | 428 | case '4': | 
|  | 429 | case '5': | 
|  | 430 | case '6': | 
|  | 431 | case '7': | 
|  | 432 | case '8': | 
|  | 433 | case '9': | 
|  | 434 | n = 0; | 
|  | 435 | do { | 
|  | 436 | APPEND_DIGIT(n, ch); | 
|  | 437 | ch = *fmt++; | 
|  | 438 | } while (is_digit(ch)); | 
|  | 439 | if (ch == '$') { | 
|  | 440 | nextarg = n; | 
|  | 441 | goto rflag; | 
|  | 442 | } | 
|  | 443 | goto reswitch; | 
|  | 444 | case 'L': | 
|  | 445 | flags |= LONGDBL; | 
|  | 446 | goto rflag; | 
|  | 447 | case 'h': | 
|  | 448 | if (*fmt == 'h') { | 
|  | 449 | fmt++; | 
|  | 450 | flags |= CHARINT; | 
|  | 451 | } else { | 
|  | 452 | flags |= SHORTINT; | 
|  | 453 | } | 
|  | 454 | goto rflag; | 
|  | 455 | case 'j': | 
|  | 456 | flags |= MAXINT; | 
|  | 457 | goto rflag; | 
|  | 458 | case 'l': | 
|  | 459 | if (*fmt == 'l') { | 
|  | 460 | fmt++; | 
|  | 461 | flags |= LLONGINT; | 
|  | 462 | } else { | 
|  | 463 | flags |= LONGINT; | 
|  | 464 | } | 
|  | 465 | goto rflag; | 
|  | 466 | case 'q': | 
|  | 467 | flags |= LLONGINT; | 
|  | 468 | goto rflag; | 
|  | 469 | case 't': | 
|  | 470 | flags |= PTRINT; | 
|  | 471 | goto rflag; | 
|  | 472 | case 'z': | 
|  | 473 | flags |= SIZEINT; | 
|  | 474 | goto rflag; | 
|  | 475 | case 'C': | 
|  | 476 | flags |= LONGINT; | 
| George Burgess IV | fa5410f | 2018-08-13 17:44:06 -0700 | [diff] [blame] | 477 | __BIONIC_FALLTHROUGH; | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 478 | case 'c': | 
|  | 479 | if (flags & LONGINT) | 
|  | 480 | ADDTYPE(T_WINT); | 
|  | 481 | else | 
|  | 482 | ADDTYPE(T_INT); | 
|  | 483 | break; | 
|  | 484 | case 'D': | 
|  | 485 | flags |= LONGINT; | 
| George Burgess IV | fa5410f | 2018-08-13 17:44:06 -0700 | [diff] [blame] | 486 | __BIONIC_FALLTHROUGH; | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 487 | case 'd': | 
|  | 488 | case 'i': | 
|  | 489 | ADDSARG(); | 
|  | 490 | break; | 
|  | 491 | case 'a': | 
|  | 492 | case 'A': | 
|  | 493 | case 'e': | 
|  | 494 | case 'E': | 
|  | 495 | case 'f': | 
|  | 496 | case 'F': | 
|  | 497 | case 'g': | 
|  | 498 | case 'G': | 
|  | 499 | if (flags & LONGDBL) | 
|  | 500 | ADDTYPE(T_LONG_DOUBLE); | 
|  | 501 | else | 
|  | 502 | ADDTYPE(T_DOUBLE); | 
|  | 503 | break; | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 504 | case 'n': | 
| Elliott Hughes | 41398d0 | 2018-03-07 13:32:58 -0800 | [diff] [blame] | 505 | __fortify_fatal("%%n not allowed on Android"); | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 506 | case 'O': | 
|  | 507 | flags |= LONGINT; | 
| George Burgess IV | fa5410f | 2018-08-13 17:44:06 -0700 | [diff] [blame] | 508 | __BIONIC_FALLTHROUGH; | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 509 | case 'o': | 
|  | 510 | ADDUARG(); | 
|  | 511 | break; | 
|  | 512 | case 'p': | 
|  | 513 | ADDTYPE(TP_VOID); | 
|  | 514 | break; | 
|  | 515 | case 'S': | 
|  | 516 | flags |= LONGINT; | 
| George Burgess IV | fa5410f | 2018-08-13 17:44:06 -0700 | [diff] [blame] | 517 | __BIONIC_FALLTHROUGH; | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 518 | case 's': | 
|  | 519 | ADDTYPE((flags & LONGINT) ? TP_WCHAR : TP_CHAR); | 
|  | 520 | break; | 
|  | 521 | case 'U': | 
|  | 522 | flags |= LONGINT; | 
| George Burgess IV | fa5410f | 2018-08-13 17:44:06 -0700 | [diff] [blame] | 523 | __BIONIC_FALLTHROUGH; | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 524 | case 'u': | 
|  | 525 | case 'X': | 
|  | 526 | case 'x': | 
| Elliott Hughes | b813a6a | 2022-08-01 22:18:40 +0000 | [diff] [blame] | 527 | case 'B': | 
|  | 528 | case 'b': | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 529 | ADDUARG(); | 
|  | 530 | break; | 
| zijunzhao | 1fdece9 | 2023-04-25 17:37:19 +0000 | [diff] [blame] | 531 | case 'w': { | 
| zijunzhao | 3b846ea | 2023-04-11 20:53:39 +0000 | [diff] [blame] | 532 | n = 0; | 
| zijunzhao | 1fdece9 | 2023-04-25 17:37:19 +0000 | [diff] [blame] | 533 | bool fast = false; | 
| zijunzhao | 3b846ea | 2023-04-11 20:53:39 +0000 | [diff] [blame] | 534 | ch = *fmt++; | 
| zijunzhao | 1fdece9 | 2023-04-25 17:37:19 +0000 | [diff] [blame] | 535 | if (ch == 'f') { | 
|  | 536 | fast = true; | 
|  | 537 | ch = *fmt++; | 
|  | 538 | } | 
| zijunzhao | 3b846ea | 2023-04-11 20:53:39 +0000 | [diff] [blame] | 539 | while (is_digit(ch)) { | 
|  | 540 | APPEND_DIGIT(n, ch); | 
|  | 541 | ch = *fmt++; | 
|  | 542 | } | 
|  | 543 | if (n == 64) { | 
|  | 544 | flags |= LLONGINT; | 
| zijunzhao | 1fdece9 | 2023-04-25 17:37:19 +0000 | [diff] [blame] | 545 | } else { | 
|  | 546 | if (n != 8 && fast) { | 
|  | 547 | #if defined(__LP64__) | 
|  | 548 | flags |= LLONGINT; | 
|  | 549 | #endif | 
|  | 550 | } | 
| zijunzhao | 3b846ea | 2023-04-11 20:53:39 +0000 | [diff] [blame] | 551 | } | 
|  | 552 | goto reswitch; | 
| zijunzhao | 1fdece9 | 2023-04-25 17:37:19 +0000 | [diff] [blame] | 553 | } | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 554 | default: /* "%?" prints ?, unless ? is NUL */ | 
|  | 555 | if (ch == '\0') goto done; | 
|  | 556 | break; | 
|  | 557 | } | 
|  | 558 | } | 
|  | 559 | done: | 
|  | 560 | /* | 
|  | 561 | * Build the argument table. | 
|  | 562 | */ | 
|  | 563 | if (tablemax >= STATIC_ARG_TBL_SIZE) { | 
|  | 564 | *argtablesiz = sizeof(union arg) * (tablemax + 1); | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 565 | *argtable = static_cast<arg*>(mmap(nullptr, *argtablesiz, | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 566 | PROT_WRITE | PROT_READ, | 
|  | 567 | MAP_ANON | MAP_PRIVATE, -1, 0)); | 
|  | 568 | if (*argtable == MAP_FAILED) return -1; | 
|  | 569 | } | 
|  | 570 |  | 
|  | 571 | for (n = 1; n <= tablemax; n++) { | 
|  | 572 | switch (typetable[n]) { | 
|  | 573 | case T_UNUSED: | 
|  | 574 | case T_CHAR: | 
|  | 575 | case T_U_CHAR: | 
|  | 576 | case T_SHORT: | 
|  | 577 | case T_U_SHORT: | 
|  | 578 | case T_INT: | 
|  | 579 | (*argtable)[n].intarg = va_arg(ap, int); | 
|  | 580 | break; | 
|  | 581 | case TP_SHORT: | 
|  | 582 | (*argtable)[n].pshortarg = va_arg(ap, short*); | 
|  | 583 | break; | 
|  | 584 | case T_U_INT: | 
|  | 585 | (*argtable)[n].uintarg = va_arg(ap, unsigned int); | 
|  | 586 | break; | 
|  | 587 | case TP_INT: | 
|  | 588 | (*argtable)[n].pintarg = va_arg(ap, int*); | 
|  | 589 | break; | 
|  | 590 | case T_LONG: | 
|  | 591 | (*argtable)[n].longarg = va_arg(ap, long); | 
|  | 592 | break; | 
|  | 593 | case T_U_LONG: | 
|  | 594 | (*argtable)[n].ulongarg = va_arg(ap, unsigned long); | 
|  | 595 | break; | 
|  | 596 | case TP_LONG: | 
|  | 597 | (*argtable)[n].plongarg = va_arg(ap, long*); | 
|  | 598 | break; | 
|  | 599 | case T_LLONG: | 
|  | 600 | (*argtable)[n].longlongarg = va_arg(ap, long long); | 
|  | 601 | break; | 
|  | 602 | case T_U_LLONG: | 
|  | 603 | (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long); | 
|  | 604 | break; | 
|  | 605 | case TP_LLONG: | 
|  | 606 | (*argtable)[n].plonglongarg = va_arg(ap, long long*); | 
|  | 607 | break; | 
|  | 608 | case T_DOUBLE: | 
|  | 609 | (*argtable)[n].doublearg = va_arg(ap, double); | 
|  | 610 | break; | 
|  | 611 | case T_LONG_DOUBLE: | 
|  | 612 | (*argtable)[n].longdoublearg = va_arg(ap, long double); | 
|  | 613 | break; | 
|  | 614 | case TP_CHAR: | 
|  | 615 | (*argtable)[n].pchararg = va_arg(ap, char*); | 
|  | 616 | break; | 
|  | 617 | case TP_VOID: | 
|  | 618 | (*argtable)[n].pvoidarg = va_arg(ap, void*); | 
|  | 619 | break; | 
|  | 620 | case T_PTRINT: | 
|  | 621 | (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t); | 
|  | 622 | break; | 
|  | 623 | case TP_PTRINT: | 
|  | 624 | (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*); | 
|  | 625 | break; | 
|  | 626 | case T_SIZEINT: | 
|  | 627 | (*argtable)[n].sizearg = va_arg(ap, size_t); | 
|  | 628 | break; | 
|  | 629 | case T_SSIZEINT: | 
|  | 630 | (*argtable)[n].ssizearg = va_arg(ap, ssize_t); | 
|  | 631 | break; | 
|  | 632 | case TP_SSIZEINT: | 
|  | 633 | (*argtable)[n].pssizearg = va_arg(ap, ssize_t*); | 
|  | 634 | break; | 
|  | 635 | case T_MAXINT: | 
|  | 636 | (*argtable)[n].intmaxarg = va_arg(ap, intmax_t); | 
|  | 637 | break; | 
|  | 638 | case T_MAXUINT: | 
|  | 639 | (*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t); | 
|  | 640 | break; | 
|  | 641 | case TP_MAXINT: | 
|  | 642 | (*argtable)[n].pintmaxarg = va_arg(ap, intmax_t*); | 
|  | 643 | break; | 
|  | 644 | case T_WINT: | 
|  | 645 | (*argtable)[n].wintarg = va_arg(ap, wint_t); | 
|  | 646 | break; | 
|  | 647 | case TP_WCHAR: | 
|  | 648 | (*argtable)[n].pwchararg = va_arg(ap, wchar_t*); | 
|  | 649 | break; | 
|  | 650 | } | 
|  | 651 | } | 
|  | 652 | goto finish; | 
|  | 653 |  | 
|  | 654 | overflow: | 
|  | 655 | errno = ENOMEM; | 
|  | 656 | ret = -1; | 
|  | 657 |  | 
|  | 658 | finish: | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 659 | if (typetable != nullptr && typetable != stattypetable) { | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 660 | munmap(typetable, *argtablesiz); | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 661 | typetable = nullptr; | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 662 | } | 
|  | 663 | return (ret); | 
|  | 664 | } | 
|  | 665 |  | 
|  | 666 | /* | 
|  | 667 | * Increase the size of the type table. | 
|  | 668 | */ | 
|  | 669 | static int __grow_type_table(unsigned char** typetable, int* tablesize) { | 
|  | 670 | unsigned char* old_table = *typetable; | 
|  | 671 | int new_size = *tablesize * 2; | 
|  | 672 |  | 
|  | 673 | if (new_size < getpagesize()) new_size = getpagesize(); | 
|  | 674 |  | 
|  | 675 | if (*tablesize == STATIC_ARG_TBL_SIZE) { | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 676 | *typetable = static_cast<unsigned char*>(mmap(nullptr, new_size, | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 677 | PROT_WRITE | PROT_READ, | 
|  | 678 | MAP_ANON | MAP_PRIVATE, -1, 0)); | 
|  | 679 | if (*typetable == MAP_FAILED) return -1; | 
|  | 680 | bcopy(old_table, *typetable, *tablesize); | 
|  | 681 | } else { | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 682 | unsigned char* new_table = static_cast<unsigned char*>(mmap(nullptr, new_size, | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 683 | PROT_WRITE | PROT_READ, | 
|  | 684 | MAP_ANON | MAP_PRIVATE, -1, 0)); | 
|  | 685 | if (new_table == MAP_FAILED) return -1; | 
|  | 686 | memmove(new_table, *typetable, *tablesize); | 
|  | 687 | munmap(*typetable, *tablesize); | 
|  | 688 | *typetable = new_table; | 
|  | 689 | } | 
|  | 690 | memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize)); | 
|  | 691 |  | 
|  | 692 | *tablesize = new_size; | 
|  | 693 | return 0; | 
|  | 694 | } | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 695 |  | 
|  | 696 | struct helpers { | 
|  | 697 | // Flush out all the vectors defined by the given uio, | 
|  | 698 | // then reset it so that it can be reused. | 
|  | 699 | static int sprint(FILE* fp, struct __suio* uio) { | 
|  | 700 | if (uio->uio_resid == 0) { | 
|  | 701 | uio->uio_iovcnt = 0; | 
|  | 702 | return 0; | 
|  | 703 | } | 
|  | 704 | int result = __sfvwrite(fp, uio); | 
|  | 705 | uio->uio_resid = 0; | 
|  | 706 | uio->uio_iovcnt = 0; | 
|  | 707 | return result; | 
|  | 708 | } | 
|  | 709 |  | 
|  | 710 | // Convert a wide character string argument for the %ls format to a multibyte | 
|  | 711 | // string representation. If not -1, prec specifies the maximum number of | 
|  | 712 | // bytes to output, and also means that we can't assume that the wide char | 
|  | 713 | // string is null-terminated. | 
|  | 714 | static char* wcsconv(wchar_t* wcsarg, int prec) { | 
|  | 715 | mbstate_t mbs; | 
|  | 716 | char buf[MB_LEN_MAX]; | 
|  | 717 | wchar_t* p; | 
|  | 718 | char* convbuf; | 
|  | 719 | size_t clen, nbytes; | 
|  | 720 |  | 
|  | 721 | // Allocate space for the maximum number of bytes we could output. | 
|  | 722 | if (prec < 0) { | 
|  | 723 | memset(&mbs, 0, sizeof(mbs)); | 
|  | 724 | p = wcsarg; | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 725 | nbytes = wcsrtombs(nullptr, (const wchar_t**)&p, 0, &mbs); | 
|  | 726 | if (nbytes == (size_t)-1) return nullptr; | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 727 | } else { | 
|  | 728 | // Optimisation: if the output precision is small enough, | 
|  | 729 | // just allocate enough memory for the maximum instead of | 
|  | 730 | // scanning the string. | 
|  | 731 | if (prec < 128) { | 
|  | 732 | nbytes = prec; | 
|  | 733 | } else { | 
|  | 734 | nbytes = 0; | 
|  | 735 | p = wcsarg; | 
|  | 736 | memset(&mbs, 0, sizeof(mbs)); | 
|  | 737 | for (;;) { | 
|  | 738 | clen = wcrtomb(buf, *p++, &mbs); | 
|  | 739 | if (clen == 0 || clen == (size_t)-1 || nbytes + clen > (size_t)prec) break; | 
|  | 740 | nbytes += clen; | 
|  | 741 | } | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 742 | if (clen == (size_t)-1) return nullptr; | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 743 | } | 
|  | 744 | } | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 745 | if ((convbuf = static_cast<char*>(malloc(nbytes + 1))) == nullptr) return nullptr; | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 746 |  | 
|  | 747 | // Fill the output buffer. | 
|  | 748 | p = wcsarg; | 
|  | 749 | memset(&mbs, 0, sizeof(mbs)); | 
|  | 750 | if ((nbytes = wcsrtombs(convbuf, (const wchar_t**)&p, nbytes, &mbs)) == (size_t)-1) { | 
|  | 751 | free(convbuf); | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 752 | return nullptr; | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 753 | } | 
|  | 754 | convbuf[nbytes] = '\0'; | 
|  | 755 | return convbuf; | 
|  | 756 | } | 
|  | 757 |  | 
|  | 758 | // Like __fputwc_unlock, but handles fake string (__SSTR) files properly. | 
|  | 759 | // File must already be locked. | 
|  | 760 | static wint_t xfputwc(wchar_t wc, FILE* fp) { | 
|  | 761 | if ((fp->_flags & __SSTR) == 0) return __fputwc_unlock(wc, fp); | 
|  | 762 |  | 
|  | 763 | char buf[MB_LEN_MAX]; | 
|  | 764 | mbstate_t mbs = {}; | 
|  | 765 | size_t len = wcrtomb(buf, wc, &mbs); | 
|  | 766 | if (len == (size_t)-1) { | 
|  | 767 | fp->_flags |= __SERR; | 
|  | 768 | errno = EILSEQ; | 
|  | 769 | return WEOF; | 
|  | 770 | } | 
|  | 771 |  | 
|  | 772 | struct __siov iov; | 
|  | 773 | iov.iov_base = buf; | 
|  | 774 | iov.iov_len = len; | 
|  | 775 | struct __suio uio; | 
|  | 776 | uio.uio_iov = &iov; | 
|  | 777 | uio.uio_resid = len; | 
|  | 778 | uio.uio_iovcnt = 1; | 
|  | 779 | return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF); | 
|  | 780 | } | 
|  | 781 |  | 
|  | 782 | // Convert a multibyte character string argument for the %s format to a wide | 
|  | 783 | // string representation. ``prec'' specifies the maximum number of bytes | 
|  | 784 | // to output. If ``prec'' is greater than or equal to zero, we can't assume | 
|  | 785 | // that the multibyte character string ends in a null character. | 
|  | 786 | // | 
|  | 787 | // Returns NULL on failure. | 
|  | 788 | // To find out what happened check errno for ENOMEM, EILSEQ and EINVAL. | 
| Elliott Hughes | 2109f12 | 2023-09-21 18:32:39 -0700 | [diff] [blame] | 789 | static wchar_t* mbsconv(const char* mbsarg, int prec) { | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 790 | mbstate_t mbs; | 
|  | 791 | const char* p; | 
|  | 792 | size_t insize, nchars, nconv; | 
|  | 793 |  | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 794 | if (mbsarg == nullptr) return nullptr; | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 795 |  | 
|  | 796 | // Supplied argument is a multibyte string; convert it to wide characters first. | 
|  | 797 | if (prec >= 0) { | 
|  | 798 | // String is not guaranteed to be NUL-terminated. Find the number of characters to print. | 
|  | 799 | p = mbsarg; | 
|  | 800 | insize = nchars = nconv = 0; | 
|  | 801 | bzero(&mbs, sizeof(mbs)); | 
|  | 802 | while (nchars != (size_t)prec) { | 
|  | 803 | nconv = mbrlen(p, MB_CUR_MAX, &mbs); | 
|  | 804 | if (nconv == (size_t)0 || nconv == (size_t)-1 || nconv == (size_t)-2) break; | 
|  | 805 | p += nconv; | 
|  | 806 | nchars++; | 
|  | 807 | insize += nconv; | 
|  | 808 | } | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 809 | if (nconv == (size_t)-1 || nconv == (size_t)-2) return (nullptr); | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 810 | } else { | 
|  | 811 | insize = strlen(mbsarg); | 
|  | 812 | } | 
|  | 813 |  | 
|  | 814 | // Allocate buffer for the result and perform the conversion, | 
|  | 815 | // converting at most `size' bytes of the input multibyte string to | 
|  | 816 | // wide characters for printing. | 
|  | 817 | wchar_t* convbuf = static_cast<wchar_t*>(calloc(insize + 1, sizeof(*convbuf))); | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 818 | if (convbuf == nullptr) return nullptr; | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 819 | wchar_t* wcp = convbuf; | 
|  | 820 | p = mbsarg; | 
|  | 821 | bzero(&mbs, sizeof(mbs)); | 
|  | 822 | nconv = 0; | 
|  | 823 | while (insize != 0) { | 
|  | 824 | nconv = mbrtowc(wcp, p, insize, &mbs); | 
|  | 825 | if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) break; | 
|  | 826 | wcp++; | 
|  | 827 | p += nconv; | 
|  | 828 | insize -= nconv; | 
|  | 829 | } | 
|  | 830 | if (nconv == (size_t)-1 || nconv == (size_t)-2) { | 
|  | 831 | free(convbuf); | 
| Yi Kong | 32bc0fc | 2018-08-02 17:31:13 -0700 | [diff] [blame] | 832 | return nullptr; | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 833 | } | 
|  | 834 | *wcp = '\0'; | 
|  | 835 |  | 
|  | 836 | return convbuf; | 
|  | 837 | } | 
|  | 838 |  | 
| zijunzhao | 1fdece9 | 2023-04-25 17:37:19 +0000 | [diff] [blame] | 839 | // Trasnlate a fixed size integer argument for the %w/%wf format to a | 
|  | 840 | // flag representation. Supported sizes are 8, 16, 32, and 64 so far. | 
|  | 841 | // See details in bionic/libc/include/stdint.h | 
|  | 842 | static int w_to_flag(int size, bool fast) { | 
|  | 843 | static constexpr int fast_size = sizeof(void*) == 8 ? LLONGINT : 0; | 
|  | 844 | if (size == 8) return CHARINT; | 
|  | 845 | if (size == 16) return fast ? fast_size : SHORTINT; | 
|  | 846 | if (size == 32) return fast ? fast_size : 0; | 
|  | 847 | if (size == 64) return LLONGINT; | 
|  | 848 | __fortify_fatal("%%w%s%d is unsupported", fast ? "f" : "", size); | 
|  | 849 | } | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 850 | }; |