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