blob: b0055f0ffdd8c47ce2520de79326127440d2637d [file] [log] [blame]
Elliott Hughes1f493172017-11-08 16:13:18 -08001/*-
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 Gao4956c372019-12-19 16:35:51 -080051#include <platform/bionic/macros.h>
George Burgess IVfa5410f2018-08-13 17:44:06 -070052
Elliott Hughes1f493172017-11-08 16:13:18 -080053#include "fvwrite.h"
54#include "gdtoa.h"
55#include "local.h"
56
57union 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.
88static 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
112static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, size_t* argtablesiz);
113static 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
121template <typename CharT>
122static 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 */
313static 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 IVfa5410f2018-08-13 17:44:06 -0700477 __BIONIC_FALLTHROUGH;
Elliott Hughes1f493172017-11-08 16:13:18 -0800478 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 IVfa5410f2018-08-13 17:44:06 -0700486 __BIONIC_FALLTHROUGH;
Elliott Hughes1f493172017-11-08 16:13:18 -0800487 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 Hughes1f493172017-11-08 16:13:18 -0800504 case 'n':
Elliott Hughes41398d02018-03-07 13:32:58 -0800505 __fortify_fatal("%%n not allowed on Android");
Elliott Hughes1f493172017-11-08 16:13:18 -0800506 case 'O':
507 flags |= LONGINT;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700508 __BIONIC_FALLTHROUGH;
Elliott Hughes1f493172017-11-08 16:13:18 -0800509 case 'o':
510 ADDUARG();
511 break;
512 case 'p':
513 ADDTYPE(TP_VOID);
514 break;
515 case 'S':
516 flags |= LONGINT;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700517 __BIONIC_FALLTHROUGH;
Elliott Hughes1f493172017-11-08 16:13:18 -0800518 case 's':
519 ADDTYPE((flags & LONGINT) ? TP_WCHAR : TP_CHAR);
520 break;
521 case 'U':
522 flags |= LONGINT;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700523 __BIONIC_FALLTHROUGH;
Elliott Hughes1f493172017-11-08 16:13:18 -0800524 case 'u':
525 case 'X':
526 case 'x':
Elliott Hughesb813a6a2022-08-01 22:18:40 +0000527 case 'B':
528 case 'b':
Elliott Hughes1f493172017-11-08 16:13:18 -0800529 ADDUARG();
530 break;
zijunzhao3b846ea2023-04-11 20:53:39 +0000531 case 'w':
532 n = 0;
533 ch = *fmt++;
534 while (is_digit(ch)) {
535 APPEND_DIGIT(n, ch);
536 ch = *fmt++;
537 }
538 if (n == 64) {
539 flags |= LLONGINT;
540 }
541 goto reswitch;
Elliott Hughes1f493172017-11-08 16:13:18 -0800542 default: /* "%?" prints ?, unless ? is NUL */
543 if (ch == '\0') goto done;
544 break;
545 }
546 }
547done:
548 /*
549 * Build the argument table.
550 */
551 if (tablemax >= STATIC_ARG_TBL_SIZE) {
552 *argtablesiz = sizeof(union arg) * (tablemax + 1);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700553 *argtable = static_cast<arg*>(mmap(nullptr, *argtablesiz,
Elliott Hughes1f493172017-11-08 16:13:18 -0800554 PROT_WRITE | PROT_READ,
555 MAP_ANON | MAP_PRIVATE, -1, 0));
556 if (*argtable == MAP_FAILED) return -1;
557 }
558
559 for (n = 1; n <= tablemax; n++) {
560 switch (typetable[n]) {
561 case T_UNUSED:
562 case T_CHAR:
563 case T_U_CHAR:
564 case T_SHORT:
565 case T_U_SHORT:
566 case T_INT:
567 (*argtable)[n].intarg = va_arg(ap, int);
568 break;
569 case TP_SHORT:
570 (*argtable)[n].pshortarg = va_arg(ap, short*);
571 break;
572 case T_U_INT:
573 (*argtable)[n].uintarg = va_arg(ap, unsigned int);
574 break;
575 case TP_INT:
576 (*argtable)[n].pintarg = va_arg(ap, int*);
577 break;
578 case T_LONG:
579 (*argtable)[n].longarg = va_arg(ap, long);
580 break;
581 case T_U_LONG:
582 (*argtable)[n].ulongarg = va_arg(ap, unsigned long);
583 break;
584 case TP_LONG:
585 (*argtable)[n].plongarg = va_arg(ap, long*);
586 break;
587 case T_LLONG:
588 (*argtable)[n].longlongarg = va_arg(ap, long long);
589 break;
590 case T_U_LLONG:
591 (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
592 break;
593 case TP_LLONG:
594 (*argtable)[n].plonglongarg = va_arg(ap, long long*);
595 break;
596 case T_DOUBLE:
597 (*argtable)[n].doublearg = va_arg(ap, double);
598 break;
599 case T_LONG_DOUBLE:
600 (*argtable)[n].longdoublearg = va_arg(ap, long double);
601 break;
602 case TP_CHAR:
603 (*argtable)[n].pchararg = va_arg(ap, char*);
604 break;
605 case TP_VOID:
606 (*argtable)[n].pvoidarg = va_arg(ap, void*);
607 break;
608 case T_PTRINT:
609 (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
610 break;
611 case TP_PTRINT:
612 (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*);
613 break;
614 case T_SIZEINT:
615 (*argtable)[n].sizearg = va_arg(ap, size_t);
616 break;
617 case T_SSIZEINT:
618 (*argtable)[n].ssizearg = va_arg(ap, ssize_t);
619 break;
620 case TP_SSIZEINT:
621 (*argtable)[n].pssizearg = va_arg(ap, ssize_t*);
622 break;
623 case T_MAXINT:
624 (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
625 break;
626 case T_MAXUINT:
627 (*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t);
628 break;
629 case TP_MAXINT:
630 (*argtable)[n].pintmaxarg = va_arg(ap, intmax_t*);
631 break;
632 case T_WINT:
633 (*argtable)[n].wintarg = va_arg(ap, wint_t);
634 break;
635 case TP_WCHAR:
636 (*argtable)[n].pwchararg = va_arg(ap, wchar_t*);
637 break;
638 }
639 }
640 goto finish;
641
642overflow:
643 errno = ENOMEM;
644 ret = -1;
645
646finish:
Yi Kong32bc0fc2018-08-02 17:31:13 -0700647 if (typetable != nullptr && typetable != stattypetable) {
Elliott Hughes1f493172017-11-08 16:13:18 -0800648 munmap(typetable, *argtablesiz);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700649 typetable = nullptr;
Elliott Hughes1f493172017-11-08 16:13:18 -0800650 }
651 return (ret);
652}
653
654/*
655 * Increase the size of the type table.
656 */
657static int __grow_type_table(unsigned char** typetable, int* tablesize) {
658 unsigned char* old_table = *typetable;
659 int new_size = *tablesize * 2;
660
661 if (new_size < getpagesize()) new_size = getpagesize();
662
663 if (*tablesize == STATIC_ARG_TBL_SIZE) {
Yi Kong32bc0fc2018-08-02 17:31:13 -0700664 *typetable = static_cast<unsigned char*>(mmap(nullptr, new_size,
Elliott Hughes1f493172017-11-08 16:13:18 -0800665 PROT_WRITE | PROT_READ,
666 MAP_ANON | MAP_PRIVATE, -1, 0));
667 if (*typetable == MAP_FAILED) return -1;
668 bcopy(old_table, *typetable, *tablesize);
669 } else {
Yi Kong32bc0fc2018-08-02 17:31:13 -0700670 unsigned char* new_table = static_cast<unsigned char*>(mmap(nullptr, new_size,
Elliott Hughes1f493172017-11-08 16:13:18 -0800671 PROT_WRITE | PROT_READ,
672 MAP_ANON | MAP_PRIVATE, -1, 0));
673 if (new_table == MAP_FAILED) return -1;
674 memmove(new_table, *typetable, *tablesize);
675 munmap(*typetable, *tablesize);
676 *typetable = new_table;
677 }
678 memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize));
679
680 *tablesize = new_size;
681 return 0;
682}
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800683
684struct helpers {
685 // Flush out all the vectors defined by the given uio,
686 // then reset it so that it can be reused.
687 static int sprint(FILE* fp, struct __suio* uio) {
688 if (uio->uio_resid == 0) {
689 uio->uio_iovcnt = 0;
690 return 0;
691 }
692 int result = __sfvwrite(fp, uio);
693 uio->uio_resid = 0;
694 uio->uio_iovcnt = 0;
695 return result;
696 }
697
698 // Convert a wide character string argument for the %ls format to a multibyte
699 // string representation. If not -1, prec specifies the maximum number of
700 // bytes to output, and also means that we can't assume that the wide char
701 // string is null-terminated.
702 static char* wcsconv(wchar_t* wcsarg, int prec) {
703 mbstate_t mbs;
704 char buf[MB_LEN_MAX];
705 wchar_t* p;
706 char* convbuf;
707 size_t clen, nbytes;
708
709 // Allocate space for the maximum number of bytes we could output.
710 if (prec < 0) {
711 memset(&mbs, 0, sizeof(mbs));
712 p = wcsarg;
Yi Kong32bc0fc2018-08-02 17:31:13 -0700713 nbytes = wcsrtombs(nullptr, (const wchar_t**)&p, 0, &mbs);
714 if (nbytes == (size_t)-1) return nullptr;
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800715 } else {
716 // Optimisation: if the output precision is small enough,
717 // just allocate enough memory for the maximum instead of
718 // scanning the string.
719 if (prec < 128) {
720 nbytes = prec;
721 } else {
722 nbytes = 0;
723 p = wcsarg;
724 memset(&mbs, 0, sizeof(mbs));
725 for (;;) {
726 clen = wcrtomb(buf, *p++, &mbs);
727 if (clen == 0 || clen == (size_t)-1 || nbytes + clen > (size_t)prec) break;
728 nbytes += clen;
729 }
Yi Kong32bc0fc2018-08-02 17:31:13 -0700730 if (clen == (size_t)-1) return nullptr;
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800731 }
732 }
Yi Kong32bc0fc2018-08-02 17:31:13 -0700733 if ((convbuf = static_cast<char*>(malloc(nbytes + 1))) == nullptr) return nullptr;
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800734
735 // Fill the output buffer.
736 p = wcsarg;
737 memset(&mbs, 0, sizeof(mbs));
738 if ((nbytes = wcsrtombs(convbuf, (const wchar_t**)&p, nbytes, &mbs)) == (size_t)-1) {
739 free(convbuf);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700740 return nullptr;
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800741 }
742 convbuf[nbytes] = '\0';
743 return convbuf;
744 }
745
746 // Like __fputwc_unlock, but handles fake string (__SSTR) files properly.
747 // File must already be locked.
748 static wint_t xfputwc(wchar_t wc, FILE* fp) {
749 if ((fp->_flags & __SSTR) == 0) return __fputwc_unlock(wc, fp);
750
751 char buf[MB_LEN_MAX];
752 mbstate_t mbs = {};
753 size_t len = wcrtomb(buf, wc, &mbs);
754 if (len == (size_t)-1) {
755 fp->_flags |= __SERR;
756 errno = EILSEQ;
757 return WEOF;
758 }
759
760 struct __siov iov;
761 iov.iov_base = buf;
762 iov.iov_len = len;
763 struct __suio uio;
764 uio.uio_iov = &iov;
765 uio.uio_resid = len;
766 uio.uio_iovcnt = 1;
767 return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF);
768 }
769
770 // Convert a multibyte character string argument for the %s format to a wide
771 // string representation. ``prec'' specifies the maximum number of bytes
772 // to output. If ``prec'' is greater than or equal to zero, we can't assume
773 // that the multibyte character string ends in a null character.
774 //
775 // Returns NULL on failure.
776 // To find out what happened check errno for ENOMEM, EILSEQ and EINVAL.
777 static wchar_t* mbsconv(char* mbsarg, int prec) {
778 mbstate_t mbs;
779 const char* p;
780 size_t insize, nchars, nconv;
781
Yi Kong32bc0fc2018-08-02 17:31:13 -0700782 if (mbsarg == nullptr) return nullptr;
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800783
784 // Supplied argument is a multibyte string; convert it to wide characters first.
785 if (prec >= 0) {
786 // String is not guaranteed to be NUL-terminated. Find the number of characters to print.
787 p = mbsarg;
788 insize = nchars = nconv = 0;
789 bzero(&mbs, sizeof(mbs));
790 while (nchars != (size_t)prec) {
791 nconv = mbrlen(p, MB_CUR_MAX, &mbs);
792 if (nconv == (size_t)0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
793 p += nconv;
794 nchars++;
795 insize += nconv;
796 }
Yi Kong32bc0fc2018-08-02 17:31:13 -0700797 if (nconv == (size_t)-1 || nconv == (size_t)-2) return (nullptr);
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800798 } else {
799 insize = strlen(mbsarg);
800 }
801
802 // Allocate buffer for the result and perform the conversion,
803 // converting at most `size' bytes of the input multibyte string to
804 // wide characters for printing.
805 wchar_t* convbuf = static_cast<wchar_t*>(calloc(insize + 1, sizeof(*convbuf)));
Yi Kong32bc0fc2018-08-02 17:31:13 -0700806 if (convbuf == nullptr) return nullptr;
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800807 wchar_t* wcp = convbuf;
808 p = mbsarg;
809 bzero(&mbs, sizeof(mbs));
810 nconv = 0;
811 while (insize != 0) {
812 nconv = mbrtowc(wcp, p, insize, &mbs);
813 if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
814 wcp++;
815 p += nconv;
816 insize -= nconv;
817 }
818 if (nconv == (size_t)-1 || nconv == (size_t)-2) {
819 free(convbuf);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700820 return nullptr;
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800821 }
822 *wcp = '\0';
823
824 return convbuf;
825 }
826
827};