/* $NetBSD: vfwprintf.c,v 1.40 2023/04/04 19:26:06 christos Exp $ */ /*- * Copyright (c) 1990, 1993 * 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 #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; __FBSDID("$FreeBSD: src/lib/libc/stdio/vfwprintf.c,v 1.27 2007/01/09 00:28:08 imp Exp $"); #else __RCSID("$NetBSD: vfwprintf.c,v 1.40 2023/04/04 19:26:06 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ /* * Actual {w,}printf innards. */ #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "reentrant.h" #include "setlocale_local.h" #include "local.h" #include "extern.h" #include "fvwrite.h" #ifndef NARROW #define MCHAR_T char #define CHAR_T wchar_t #define STRLEN(a) wcslen(a) #define MEMCHR(a, b, c) wmemchr(a, b, c) #define SCONV(a, b, loc) __mbsconv(a, b, loc) #define STRCONST(a) L ## a #define WDECL(a, b) a ## w ## b #define END_OF_FILE WEOF #define MULTI 0 #else #define MCHAR_T wchar_t #define CHAR_T char #define STRLEN(a) strlen(a) #define MEMCHR(a, b, c) memchr(a, b, c) #define SCONV(a, b, loc) __wcsconv(a, b, loc) #define STRCONST(a) a #define WDECL(a, b) a ## b #define END_OF_FILE EOF #define MULTI LONGINT #endif union arg { int intarg; u_int uintarg; long longarg; u_long ulongarg; long long longlongarg; unsigned long long ulonglongarg; ptrdiff_t ptrdiffarg; ssize_t ssizearg; size_t sizearg; 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; size_t *psizearg; intmax_t *pintmaxarg; #ifndef NO_FLOATING_POINT double doublearg; long double longdoublearg; #endif wint_t wintarg; wchar_t *pwchararg; }; /* * Type ids for argument type table. */ enum typeid { T_UNUSED = 0, TP_SHORT, T_INT, T_U_INT, TP_INT, T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SIZET, T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR, T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR }; #ifdef NARROW __printflike(3, 0) #endif static int __sbprintf(FILE *, locale_t, const CHAR_T *, va_list); static CHAR_T *__ujtoa(uintmax_t, CHAR_T *, int, int, const char *, int, char, const char *); static CHAR_T *__ultoa(u_long, CHAR_T *, int, int, const char *, int, char, const char *); #ifndef NARROW static CHAR_T *__mbsconv(char *, int, locale_t); static wint_t __xfputwc(CHAR_T, FILE *, locale_t); #else static char *__wcsconv(wchar_t *, int, locale_t); static int __sprint(FILE *, struct __suio *); #endif static int __find_arguments(const CHAR_T *, va_list, union arg **); static int __grow_type_table(size_t, enum typeid **, size_t *); /* * 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, locale_t loc, const CHAR_T *fmt, va_list ap) { int ret; FILE fake; struct __sfileext fakeext; unsigned char buf[BUFSIZ]; _DIAGASSERT(fp != NULL); _DIAGASSERT(fmt != NULL); _FILEEXT_SETUP(&fake, &fakeext); memset(WCIO_GET(&fake), 0, sizeof(struct wchar_io_data)); /* copy the important variables */ fake._flags = fp->_flags & ~__SNBF; fake._file = fp->_file; fake._cookie = fp->_cookie; fake._write = fp->_write; fake._flush = fp->_flush; /* 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 */ ret = WDECL(__vf,printf_unlocked_l)(&fake, loc, fmt, ap); if (ret >= 0 && fflush(&fake)) ret = END_OF_FILE; if (fake._flags & __SERR) fp->_flags |= __SERR; return ret; } #ifndef NARROW /* * Like __fputwc, but handles fake string (__SSTR) files properly. * File must already be locked. */ static wint_t __xfputwc(wchar_t wc, FILE *fp, locale_t loc) { static const mbstate_t initial; mbstate_t mbs; char buf[MB_LEN_MAX]; struct __suio uio; struct __siov iov; size_t len; if ((fp->_flags & __SSTR) == 0) return __fputwc_unlock(wc, fp); mbs = initial; if ((len = wcrtomb_l(buf, wc, &mbs, loc)) == (size_t)-1) { fp->_flags |= __SERR; return END_OF_FILE; } uio.uio_iov = &iov; uio.uio_resid = len; uio.uio_iovcnt = 1; iov.iov_base = buf; iov.iov_len = len; return __sfvwrite(fp, &uio) != EOF ? (wint_t)wc : END_OF_FILE; } #else /* * 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) { int err; _DIAGASSERT(fp != NULL); _DIAGASSERT(uio != NULL); if (uio->uio_resid == 0) { uio->uio_iovcnt = 0; return 0; } err = __sfvwrite(fp, uio); uio->uio_resid = 0; uio->uio_iovcnt = 0; return err; } #endif /* * Macros for converting digits to letters and vice versa */ #define to_digit(c) ((c) - '0') #define is_digit(c) ((unsigned)to_digit(c) <= 9) #define to_char(n) (CHAR_T)((n) + '0') /* * Convert an unsigned long to ASCII for printf purposes, returning * a pointer to the first character of the string representation. * Octal numbers can be forced to have a leading zero; hex numbers * use the given digits. */ static CHAR_T * __ultoa(u_long val, CHAR_T *endp, int base, int octzero, const char *xdigs, int needgrp, char thousep, const char *grp) { CHAR_T *cp = endp; long sval; int ndig; /* * Handle the three cases separately, in the hope of getting * better/faster code. */ switch (base) { case 10: if (val < 10) { /* many numbers are 1 digit */ *--cp = to_char(val); return cp; } ndig = 0; /* * On many machines, unsigned arithmetic is harder than * signed arithmetic, so we do at most one unsigned mod and * divide; this is sufficient to reduce the range of * the incoming value to where signed arithmetic works. */ if (val > LONG_MAX) { *--cp = to_char(val % 10); ndig++; sval = val / 10; } else sval = val; do { *--cp = to_char(sval % 10); ndig++; /* * If (*grp == CHAR_MAX) then no more grouping * should be performed. */ if (needgrp && ndig == *grp && (unsigned char)*grp != (unsigned char)CHAR_MAX && sval > 9) { *--cp = thousep; ndig = 0; /* * If (*(grp+1) == '\0') then we have to * use *grp character (last grouping rule) * for all next cases */ if (*(grp+1) != '\0') grp++; } sval /= 10; } while (sval != 0); break; case 8: do { *--cp = to_char(val & 7); val >>= 3; } while (val); if (octzero && *cp != '0') *--cp = '0'; break; case 16: do { *--cp = xdigs[(size_t)val & 15]; val >>= 4; } while (val); break; default: /* oops */ abort(); } return cp; } /* Identical to __ultoa, but for intmax_t. */ static CHAR_T * __ujtoa(uintmax_t val, CHAR_T *endp, int base, int octzero, const char *xdigs, int needgrp, char thousep, const char *grp) { CHAR_T *cp = endp; intmax_t sval; int ndig; /* quick test for small values; __ultoa is typically much faster */ /* (perhaps instead we should run until small, then call __ultoa?) */ if (val <= ULONG_MAX) return __ultoa((u_long)val, endp, base, octzero, xdigs, needgrp, thousep, grp); switch (base) { case 10: if (val < 10) { *--cp = to_char(val % 10); return cp; } ndig = 0; if (val > INTMAX_MAX) { *--cp = to_char(val % 10); ndig++; sval = val / 10; } else sval = val; do { *--cp = to_char(sval % 10); ndig++; /* * If (*grp == CHAR_MAX) then no more grouping * should be performed. */ if (needgrp && (unsigned char)*grp != (unsigned char)CHAR_MAX && ndig == *grp && sval > 9) { *--cp = thousep; ndig = 0; /* * If (*(grp+1) == '\0') then we have to * use *grp character (last grouping rule) * for all next cases */ if (*(grp+1) != '\0') grp++; } sval /= 10; } while (sval != 0); break; case 8: do { *--cp = to_char(val & 7); val >>= 3; } while (val); if (octzero && *cp != '0') *--cp = '0'; break; case 16: do { *--cp = xdigs[(size_t)val & 15]; val >>= 4; } while (val); break; default: abort(); } return cp; } #ifndef NARROW /* * 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 char. string ends in a null character. */ static wchar_t * __mbsconv(char *mbsarg, int prec, locale_t loc) { static const mbstate_t initial; mbstate_t mbs; wchar_t *convbuf, *wcp; const char *p; size_t insize, nchars, nconv; if (mbsarg == NULL) return NULL; /* * 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; mbs = initial; while (nchars != (size_t)prec) { nconv = mbrlen_l(p, MB_CUR_MAX_L(loc), &mbs, loc); if (nconv == 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 NULL; } 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. */ convbuf = NULL; errno = reallocarr(&convbuf, insize + 1, sizeof(*convbuf)); if (errno) return NULL; wcp = convbuf; p = mbsarg; mbs = initial; nconv = 0; while (insize != 0) { nconv = mbrtowc_l(wcp, p, insize, &mbs, loc); 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) { int serrno = errno; free(convbuf); errno = serrno; return NULL; } *wcp = L'\0'; return convbuf; } #else /* * 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 ends is null-terminated. */ static char * __wcsconv(wchar_t *wcsarg, int prec, locale_t loc) { static const mbstate_t initial; 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) { p = wcsarg; mbs = initial; nbytes = wcsrtombs_l(NULL, (void *)&p, 0, &mbs, loc); if (nbytes == (size_t)-1) return NULL; } 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; mbs = initial; for (;;) { clen = wcrtomb_l(buf, *p++, &mbs, loc); if (clen == 0 || clen == (size_t)-1 || nbytes + clen > (size_t)prec) break; nbytes += clen; } } } if ((convbuf = malloc(nbytes + 1)) == NULL) return NULL; /* Fill the output buffer. */ p = wcsarg; mbs = initial; if ((nbytes = wcsrtombs_l(convbuf, (void *)&p, nbytes, &mbs, loc)) == (size_t)-1) { free(convbuf); return NULL; } convbuf[nbytes] = '\0'; return convbuf; } #endif /* * MT-safe version */ int WDECL(vf,printf)(FILE * __restrict fp, const CHAR_T * __restrict fmt0, va_list ap) { int ret; FLOCKFILE(fp); ret = WDECL(__vf,printf_unlocked_l)(fp, _current_locale(), fmt0, ap); FUNLOCKFILE(fp); return ret; } int WDECL(vf,printf_l)(FILE * __restrict fp, locale_t loc, const CHAR_T * __restrict fmt0, va_list ap) { int ret; FLOCKFILE(fp); ret = WDECL(__vf,printf_unlocked_l)(fp, loc, fmt0, ap); FUNLOCKFILE(fp); return ret; } #ifndef NO_FLOATING_POINT #include #include #include "floatio.h" #define DEFPREC 6 static int exponent(CHAR_T *, int, int); #endif /* !NO_FLOATING_POINT */ /* * 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 0x001 /* alternate form */ #define LADJUST 0x004 /* left adjustment */ #define LONGDBL 0x008 /* long double */ #define LONGINT 0x010 /* long integer */ #define LLONGINT 0x020 /* long long integer */ #define SHORTINT 0x040 /* short integer */ #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ #define FPT 0x100 /* Floating point number */ #define GROUPING 0x200 /* use grouping ("'" flag) */ /* C99 additional size modifiers: */ #define SIZET 0x400 /* size_t */ #define PTRDIFFT 0x800 /* ptrdiff_t */ #define INTMAXT 0x1000 /* intmax_t */ #define CHARINT 0x2000 /* print char using int format */ /* * Non-MT-safe version */ int WDECL(__vf,printf_unlocked_l)(FILE *fp, locale_t loc, const CHAR_T *fmt0, va_list ap) { CHAR_T *fmt; /* format string */ int ch; /* character from fmt */ int n, n2; /* handy integer (short term usage) */ CHAR_T *cp; /* handy char pointer (short term usage) */ int flags; /* flags as above */ int ret; /* return value accumulator */ int width; /* width from format (%8d), or 0 */ int prec; /* precision from format; <0 for N/A */ CHAR_T sign; /* sign prefix (' ', '+', '-', or \0) */ char thousands_sep; /* locale specific thousands separator */ const char *grouping; /* locale specific numeric grouping rules */ #ifndef NO_FLOATING_POINT /* * We can decompose the printed representation of floating * point numbers into several parts, some of which may be empty: * * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ * A B ---C--- D E F * * A: 'sign' holds this value if present; '\0' otherwise * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal * C: cp points to the string MMMNNN. Leading and trailing * zeros are not in the string and must be added. * D: expchar holds this character; '\0' if no exponent, e.g. %f * F: at least two digits for decimal, at least one digit for hex */ char *decimal_point; /* locale specific decimal point */ int signflag; /* true if float is negative */ union { /* floating point arguments %[aAeEfFgG] */ double dbl; #ifdef WIDE_DOUBLE long double ldbl; #endif } fparg; char *dtoaend; /* pointer to end of converted digits */ char *dtoaresult; /* buffer allocated by dtoa */ int expt; /* integer value of exponent */ char expchar; /* exponent character: [eEpP\0] */ int expsize; /* character count for expstr */ int lead; /* sig figs before decimal or group sep */ int ndig; /* actual number of digits returned by dtoa */ CHAR_T expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ int nseps; /* number of group separators with ' */ int nrepeats; /* number of repeats of the last group */ #endif u_long ulval; /* integer arguments %[diouxX] */ uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ int base; /* base for [diouxX] conversion */ int dprec; /* a copy of prec if [diouxX], 0 otherwise */ int realsz; /* field size expanded by dprec, sign, etc */ int size; /* size of converted field or string */ int prsize; /* max size of printed field */ const char *xdigs; /* digits for %[xX] conversion */ #ifdef NARROW #define NIOV 8 struct __siov *iovp; /* for PRINT macro */ struct __suio uio; /* output information: summary */ struct __siov iov[NIOV];/* ... and individual io vectors */ #else int n3; #endif CHAR_T buf[BUF]; /* buffer with space for digits of uintmax_t */ CHAR_T ox[2]; /* space for 0x hex-prefix */ union arg *argtable; /* args, built due to positional arg */ union arg statargtable [STATIC_ARG_TBL_SIZE]; int nextarg; /* 1-based argument index */ va_list orgap; /* original argument pointer */ CHAR_T *convbuf; /* multibyte to wide conversion result */ /* * Choose PADSIZE to trade efficiency vs. size. If larger printf * fields occur frequently, increase PADSIZE and make the initialisers * below longer. */ #define PADSIZE 16 /* pad chunk size */ static CHAR_T blanks[PADSIZE] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; static CHAR_T zeroes[PADSIZE] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; static const char xdigs_lower[16] = "0123456789abcdef"; static const char xdigs_upper[16] = "0123456789ABCDEF"; /* * BEWARE, these `goto error' on error, PRINT uses `n2' and * PAD uses `n'. */ #ifndef NARROW #define PRINT(ptr, len) do { \ for (n3 = 0; n3 < (len); n3++) \ if (__xfputwc((ptr)[n3], fp, loc) == END_OF_FILE) { \ fp->_flags |= __SERR; \ goto error; \ } \ } while (0) #define FLUSH() #else #define PRINT(ptr, len) do { \ iovp->iov_base = __UNCONST(ptr); \ iovp->iov_len = (len); \ uio.uio_resid += (len); \ iovp++; \ if (++uio.uio_iovcnt >= NIOV) { \ if (__sprint(fp, &uio)) \ goto error; \ iovp = iov; \ } \ } while (0) #define FLUSH() do { \ if (uio.uio_resid && __sprint(fp, &uio)) \ goto error; \ uio.uio_iovcnt = 0; \ iovp = iov; \ } while (0) #endif /* NARROW */ #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 { \ ptrdiff_t td = (ep) - (p); \ _DIAGASSERT(__type_fit(int, td)); \ n2 = (int)td; \ if (n2 > (len)) \ n2 = (len); \ if (n2 > 0) \ PRINT((p), n2); \ PAD((len) - (n2 > 0 ? n2 : 0), (with)); \ } while (0) /* * 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) \ ((/*CONSTCOND*/argtable != NULL) ? *((type*)(void*)(&argtable[nextarg++])) : \ (nextarg++, va_arg(ap, type))) /* * To extend shorts properly, we need both signed and unsigned * argument extraction methods. */ #define SARG() \ (flags&LONGINT ? GETARG(long) : \ flags&SHORTINT ? (long)(short)GETARG(int) : \ flags&CHARINT ? (long)(signed char)GETARG(int) : \ (long)GETARG(int)) #define UARG() \ (flags&LONGINT ? GETARG(u_long) : \ flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ (u_long)GETARG(u_int)) #define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) #define SJARG() \ (flags&INTMAXT ? GETARG(intmax_t) : \ flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ (intmax_t)GETARG(long long)) #define UJARG() \ (flags&INTMAXT ? GETARG(uintmax_t) : \ flags&SIZET ? (uintmax_t)GETARG(size_t) : \ flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ (uintmax_t)GETARG(unsigned long long)) /* * 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)) { \ n2 = 10 * n2 + to_digit(*cp); \ cp++; \ } \ if (*cp == '$') { \ int hold = nextarg; \ if (argtable == NULL) { \ argtable = statargtable; \ if (__find_arguments(fmt0, orgap, &argtable) == -1) \ goto oomem; \ } \ nextarg = n2; \ val = GETARG (int); \ nextarg = hold; \ fmt = ++cp; \ } else { \ val = GETARG (int); \ } _DIAGASSERT(fp != NULL); _DIAGASSERT(fmt0 != NULL); _SET_ORIENTATION(fp, -1); thousands_sep = '\0'; grouping = NULL; #ifndef NO_FLOATING_POINT decimal_point = localeconv_l(loc)->decimal_point; expsize = 0; /* XXXGCC -Wuninitialized [sh3,m68000] */ ndig = -1; /* XXX gcc */ #endif convbuf = NULL; /* sorry, f{w,}printf(read_only_file, L"") returns {W,}EOF, not 0 */ if (cantwrite(fp)) { errno = EBADF; return END_OF_FILE; } /* optimise fprintf(stderr) (and other unbuffered Unix files) */ if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && __sfileno(fp) != -1) return __sbprintf(fp, loc, fmt0, ap); fmt = (CHAR_T *)__UNCONST(fmt0); argtable = NULL; nextarg = 1; va_copy(orgap, ap); #ifdef NARROW uio.uio_iov = iovp = iov; uio.uio_resid = 0; uio.uio_iovcnt = 0; #endif ret = 0; /* * Scan the format for conversions (`%' character). */ for (;;) { const CHAR_T *result; for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; _DIAGASSERT(__type_fit(int, fmt - cp)); if ((n = (int)(fmt - cp)) != 0) { if ((unsigned)ret + n > INT_MAX) { ret = END_OF_FILE; goto error; } PRINT(cp, n); ret += n; } if (ch == '\0') goto done; fmt++; /* skip over '%' */ flags = 0; dprec = 0; width = 0; prec = -1; sign = '\0'; ox[1] = '\0'; #ifndef NO_FLOATING_POINT expchar = '\0'; lead = 0; nseps = nrepeats = 0; #endif ulval = 0; ujval = 0; xdigs = NULL; rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': /*- * ``If the space and + flags both appear, the space * flag will be ignored.'' * -- ANSI X3J11 */ if (!sign) sign = ' '; goto rflag; case '#': flags |= ALT; goto rflag; case '*': /*- * ``A negative field width argument is taken as a * - flag followed by a positive field width.'' * -- ANSI X3J11 * They don't exclude field widths read from args. */ GETASTER (width); if (width >= 0) goto rflag; width = -width; /* FALLTHROUGH */ case '-': flags |= LADJUST; goto rflag; case '+': sign = '+'; goto rflag; case '\'': thousands_sep = *(localeconv_l(loc)->thousands_sep); grouping = localeconv_l(loc)->grouping; /* Use grouping if defined by locale */ if (thousands_sep && grouping && *grouping) flags |= GROUPING; else { thousands_sep = '\0'; grouping = NULL; } goto rflag; case '.': if ((ch = *fmt++) == '*') { GETASTER (prec); goto rflag; } prec = 0; while (is_digit(ch)) { prec = 10 * prec + to_digit(ch); ch = *fmt++; } goto reswitch; case '0': /*- * ``Note that 0 is taken as a flag, not as the * beginning of a field width.'' * -- ANSI X3J11 */ flags |= ZEROPAD; goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { n = 10 * n + to_digit(ch); ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { nextarg = n; if (argtable == NULL) { argtable = statargtable; if (__find_arguments(fmt0, orgap, &argtable) == -1) goto oomem; } goto rflag; } width = n; goto reswitch; #ifndef NO_FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': if (flags & SHORTINT) { flags &= ~SHORTINT; flags |= CHARINT; } else flags |= SHORTINT; goto rflag; case 'j': flags |= INTMAXT; goto rflag; case 'l': if (flags & LONGINT) { flags &= ~LONGINT; flags |= LLONGINT; } else flags |= LONGINT; goto rflag; case 'q': flags |= LLONGINT; /* not necessarily */ goto rflag; case 't': flags |= PTRDIFFT; goto rflag; case 'z': flags |= SIZET; goto rflag; case 'C': flags |= LONGINT; /*FALLTHROUGH*/ case 'c': #ifdef NARROW if (flags & LONGINT) { static const mbstate_t initial; mbstate_t mbs; size_t mbseqlen; mbs = initial; mbseqlen = wcrtomb_l(buf, (wchar_t)GETARG(wint_t), &mbs, loc); if (mbseqlen == (size_t)-1) { fp->_flags |= __SERR; goto error; } size = (int)mbseqlen; } else { *buf = GETARG(int); size = 1; } #else if (flags & LONGINT) *buf = (wchar_t)GETARG(wint_t); else *buf = (wchar_t)btowc_l(GETARG(int), loc); size = 1; #endif result = buf; sign = '\0'; break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': if (flags & INTMAX_SIZE) { ujval = SJARG(); if ((intmax_t)ujval < 0) { ujval = -ujval; sign = '-'; } } else { ulval = SARG(); if ((long)ulval < 0) { ulval = -ulval; sign = '-'; } } base = 10; goto number; #ifndef NO_FLOATING_POINT case 'a': case 'A': if (ch == 'a') { ox[1] = 'x'; xdigs = xdigs_lower; expchar = 'p'; } else { ox[1] = 'X'; xdigs = xdigs_upper; expchar = 'P'; } if (prec >= 0) prec++; #ifdef WIDE_DOUBLE if (flags & LONGDBL) { fparg.ldbl = GETARG(long double); dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend); } else #endif { fparg.dbl = GETARG(double); dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend); } if (dtoaresult == NULL) goto oomem; if (prec < 0) { _DIAGASSERT(__type_fit(int, dtoaend - dtoaresult)); prec = (int)(dtoaend - dtoaresult); } if (expt == INT_MAX) ox[1] = '\0'; _DIAGASSERT(__type_fit(int, dtoaend - dtoaresult)); ndig = (int)(dtoaend - dtoaresult); if (convbuf != NULL) free(convbuf); #ifndef NARROW result = convbuf = __mbsconv(dtoaresult, -1, loc); #else /*XXX inefficient*/ result = convbuf = strdup(dtoaresult); #endif if (result == NULL) goto oomem; __freedtoa(dtoaresult); goto fp_common; case 'e': case 'E': expchar = ch; if (prec < 0) /* account for digit before decpt */ prec = DEFPREC + 1; else prec++; goto fp_begin; case 'f': case 'F': expchar = '\0'; goto fp_begin; case 'g': case 'G': expchar = ch - ('g' - 'e'); if (prec == 0) prec = 1; fp_begin: if (prec < 0) prec = DEFPREC; #ifdef WIDE_DOUBLE if (flags & LONGDBL) { fparg.ldbl = GETARG(long double); dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); } else #endif { fparg.dbl = GETARG(double); dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); if (expt == 9999) expt = INT_MAX; } if (dtoaresult == NULL) goto oomem; _DIAGASSERT(__type_fit(int, dtoaend - dtoaresult)); ndig = (int)(dtoaend - dtoaresult); if (convbuf != NULL) free(convbuf); #ifndef NARROW result = convbuf = __mbsconv(dtoaresult, -1, loc); #else /*XXX inefficient*/ result = convbuf = strdup(dtoaresult); #endif if (result == NULL) goto oomem; __freedtoa(dtoaresult); fp_common: if (signflag) sign = '-'; if (expt == INT_MAX) { /* inf or nan */ if (*result == 'N') { result = (ch >= 'a') ? STRCONST("nan") : STRCONST("NAN"); sign = '\0'; } else result = (ch >= 'a') ? STRCONST("inf") : STRCONST("INF"); size = 3; flags &= ~ZEROPAD; break; } flags |= FPT; if (ch == 'g' || ch == 'G') { if (expt > -4 && expt <= prec) { /* Make %[gG] smell like %[fF] */ expchar = '\0'; if (flags & ALT) prec -= expt; else prec = ndig - expt; if (prec < 0) prec = 0; } else { /* * Make %[gG] smell like %[eE], but * trim trailing zeroes if no # flag. */ if (!(flags & ALT)) prec = ndig; } } if (expchar) { expsize = exponent(expstr, expt - 1, expchar); size = expsize + prec; if (prec > 1 || flags & ALT) ++size; } else { /* space for digits before decimal point */ if (expt > 0) size = expt; else /* "0" */ size = 1; /* space for decimal pt and following digits */ if (prec || flags & ALT) size += prec + 1; if (grouping && expt > 0) { /* space for thousands' grouping */ nseps = nrepeats = 0; lead = expt; while ((unsigned char)*grouping != (unsigned char)CHAR_MAX) { if (lead <= *grouping) break; lead -= *grouping; if (*(grouping+1)) { nseps++; grouping++; } else nrepeats++; } size += nseps + nrepeats; } else lead = expt; } break; #endif /* !NO_FLOATING_POINT */ case 'n': /* * Assignment-like behavior is specified if the * value overflows or is otherwise unrepresentable. * C99 says to use `signed char' for %hhn conversions. */ if (flags & LLONGINT) *GETARG(long long *) = ret; else if (flags & SIZET) *GETARG(ssize_t *) = (ssize_t)ret; else if (flags & PTRDIFFT) *GETARG(ptrdiff_t *) = ret; else if (flags & INTMAXT) *GETARG(intmax_t *) = ret; else if (flags & LONGINT) *GETARG(long *) = ret; else if (flags & SHORTINT) *GETARG(short *) = ret; else if (flags & CHARINT) *GETARG(signed char *) = ret; else *GETARG(int *) = ret; continue; /* no output */ case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': if (flags & INTMAX_SIZE) ujval = UJARG(); else ulval = UARG(); base = 8; goto nosign; case 'p': /*- * ``The argument shall be a pointer to void. The * value of the pointer is converted to a sequence * of printable characters, in an implementation- * defined manner.'' * -- ANSI X3J11 */ ujval = (uintmax_t)(uintptr_t)GETARG(void *); base = 16; xdigs = xdigs_lower; flags = flags | INTMAXT; ox[1] = 'x'; goto nosign; case 'S': flags |= LONGINT; /*FALLTHROUGH*/ case 's': if ((flags & LONGINT) != MULTI) { if ((result = GETARG(CHAR_T *)) == NULL) result = STRCONST("(null)"); } else { MCHAR_T *mc; if (convbuf != NULL) free(convbuf); if ((mc = GETARG(MCHAR_T *)) == NULL) result = STRCONST("(null)"); else { convbuf = SCONV(mc, prec, loc); if (convbuf == NULL) { fp->_flags |= __SERR; goto error; } result = convbuf; } } if (prec >= 0) { /* * can't use STRLEN; can only look for the * NUL in the first `prec' characters, and * STRLEN() will go further. */ CHAR_T *p = MEMCHR(result, 0, (size_t)prec); if (p != NULL) { _DIAGASSERT(__type_fit(int, p - result)); size = (int)(p - result); if (size > prec) size = prec; } else size = prec; } else { size_t rlen = STRLEN(result); _DIAGASSERT(__type_fit(int, rlen)); size = (int)rlen; } sign = '\0'; break; case 'U': flags |= LONGINT; /*FALLTHROUGH*/ case 'u': if (flags & INTMAX_SIZE) ujval = UJARG(); else ulval = UARG(); base = 10; goto nosign; case 'X': xdigs = xdigs_upper; goto hex; case 'x': xdigs = xdigs_lower; hex: if (flags & INTMAX_SIZE) ujval = UJARG(); else ulval = UARG(); base = 16; /* leading 0x/X only if non-zero */ if (flags & ALT && (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) ox[1] = ch; flags &= ~GROUPING; /* unsigned conversions */ nosign: sign = '\0'; /*- * ``... diouXx conversions ... if a precision is * specified, the 0 flag will be ignored.'' * -- ANSI X3J11 */ number: if ((dprec = prec) >= 0) flags &= ~ZEROPAD; /*- * ``The result of converting a zero value with an * explicit precision of zero is no characters.'' * -- ANSI X3J11 * * ``The C Standard is clear enough as is. The call * printf("%#.0o", 0) should print 0.'' * -- Defect Report #151 */ result = cp = buf + BUF; if (flags & INTMAX_SIZE) { if (ujval != 0 || prec != 0 || (flags & ALT && base == 8)) result = __ujtoa(ujval, cp, base, flags & ALT, xdigs, flags & GROUPING, thousands_sep, grouping); } else { if (ulval != 0 || prec != 0 || (flags & ALT && base == 8)) result = __ultoa(ulval, cp, base, flags & ALT, xdigs, flags & GROUPING, thousands_sep, grouping); } _DIAGASSERT(__type_fit(int, buf + BUF - result)); size = (int)(buf + BUF - result); if (size > BUF) /* should never happen */ abort(); break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; /* pretend it was %c with argument ch */ *buf = ch; result = buf; size = 1; sign = '\0'; break; } /* * All reasonable formats wind up here. At this point, `result' * points to a string which (if not flags&LADJUST) should be * padded out to `width' places. If flags&ZEROPAD, it should * first be prefixed by any sign or other prefix; otherwise, * it should be blank padded before the prefix is emitted. * After any left-hand padding and prefixing, emit zeroes * required by a decimal [diouxX] precision, then print the * string proper, then emit zeroes required by any leftover * floating precision; finally, if LADJUST, pad with blanks. * * Compute actual size, so we know how much to pad. * size excludes decimal prec; realsz includes it. */ realsz = dprec > size ? dprec : size; if (sign) realsz++; if (ox[1]) realsz += 2; prsize = width > realsz ? width : realsz; if ((unsigned)ret + prsize > INT_MAX) { ret = END_OF_FILE; goto error; } /* right-adjusting blank padding */ if ((flags & (LADJUST|ZEROPAD)) == 0) PAD(width - realsz, blanks); /* prefix */ if (sign) PRINT(&sign, 1); if (ox[1]) { /* ox[1] is either x, X, or \0 */ ox[0] = '0'; PRINT(ox, 2); } /* right-adjusting zero padding */ if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes); /* leading zeroes from decimal precision */ PAD(dprec - size, zeroes); /* the string or number proper */ #ifndef NO_FLOATING_POINT if ((flags & FPT) == 0) { PRINT(result, size); } else { /* glue together f_p fragments */ if (!expchar) { /* %[fF] or sufficiently short %[gG] */ if (expt <= 0) { PRINT(zeroes, 1); if (prec || flags & ALT) PRINT(decimal_point, 1); PAD(-expt, zeroes); /* already handled initial 0's */ prec += expt; } else { PRINTANDPAD(result, convbuf + ndig, lead, zeroes); result += lead; if (grouping) { while (nseps>0 || nrepeats>0) { if (nrepeats > 0) nrepeats--; else { grouping--; nseps--; } PRINT(&thousands_sep, 1); PRINTANDPAD(result, convbuf + ndig, *grouping, zeroes); result += *grouping; } if (result > convbuf + ndig) result = convbuf + ndig; } if (prec || flags & ALT) { buf[0] = *decimal_point; PRINT(buf, 1); } } PRINTANDPAD(result, convbuf + ndig, prec, zeroes); } else { /* %[eE] or sufficiently long %[gG] */ if (prec > 1 || flags & ALT) { buf[0] = *result++; buf[1] = *decimal_point; PRINT(buf, 2); PRINT(result, ndig-1); PAD(prec - ndig, zeroes); } else /* XeYYY */ PRINT(result, 1); PRINT(expstr, expsize); } } #else PRINT(result, size); #endif /* left-adjusting padding (always blank) */ if (flags & LADJUST) PAD(width - realsz, blanks); /* finally, adjust ret */ ret += prsize; FLUSH(); } done: FLUSH(); error: va_end(orgap); if (convbuf != NULL) free(convbuf); if (__sferror(fp)) ret = END_OF_FILE; if ((argtable != NULL) && (argtable != statargtable)) free (argtable); return ret; /* NOTREACHED */ oomem: errno = ENOMEM; ret = END_OF_FILE; goto error; } /* * 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 replaces with a malloc-ed one if it overflows. */ static int __find_arguments(const CHAR_T *fmt0, va_list ap, union arg **argtable) { CHAR_T *fmt; /* format string */ int ch; /* character from fmt */ size_t n, n2; /* handy index (short term usage) */ CHAR_T *cp; /* handy char pointer (short term usage) */ int flags; /* flags as above */ enum typeid *typetable; /* table of types */ enum typeid stattypetable [STATIC_ARG_TBL_SIZE]; size_t tablesize; /* current size of type table */ size_t tablemax; /* largest used index in table */ size_t nextarg; /* 1-based argument index */ size_t nitems; /* number of items we picked from the stack */ /* * Add an argument type to the table, expanding if necessary. * Check for overflow. */ #define ADDTYPE(type) \ do { \ if (nextarg > SIZE_MAX / sizeof(**argtable)) { \ if (typetable != stattypetable) \ free(typetable); \ return -1; \ } \ if (nextarg >= tablesize) \ if (__grow_type_table(nextarg, &typetable, \ &tablesize) == -1) \ return -1; \ if (nextarg > tablemax) \ tablemax = nextarg; \ typetable[nextarg++] = type; \ nitems++; \ } while (0) #define ADDSARG() \ do { \ if (flags & INTMAXT) \ ADDTYPE(T_INTMAXT); \ else if (flags & SIZET) \ ADDTYPE(T_SSIZET); \ else if (flags & PTRDIFFT) \ ADDTYPE(T_PTRDIFFT); \ else if (flags & LLONGINT) \ ADDTYPE(T_LLONG); \ else if (flags & LONGINT) \ ADDTYPE(T_LONG); \ else \ ADDTYPE(T_INT); \ } while (0) #define ADDUARG() \ do { \ if (flags & INTMAXT) \ ADDTYPE(T_UINTMAXT); \ else if (flags & SIZET) \ ADDTYPE(T_SIZET); \ else if (flags & PTRDIFFT) \ ADDTYPE(T_PTRDIFFT); \ else if (flags & LLONGINT) \ ADDTYPE(T_U_LLONG); \ else if (flags & LONGINT) \ ADDTYPE(T_U_LONG); \ else \ ADDTYPE(T_U_INT); \ } while (0) /* * Add * arguments to the type array. */ #define ADDASTER() \ n2 = 0; \ cp = fmt; \ while (is_digit(*cp)) { \ n2 = 10 * n2 + to_digit(*cp); \ cp++; \ } \ if (*cp == '$') { \ size_t hold = nextarg; \ nextarg = n2; \ ADDTYPE(T_INT); \ nextarg = hold; \ fmt = ++cp; \ } else { \ ADDTYPE(T_INT); \ } fmt = (CHAR_T *)__UNCONST(fmt0); memset(stattypetable, 0, sizeof(stattypetable)); typetable = stattypetable; tablesize = STATIC_ARG_TBL_SIZE; tablemax = 0; nextarg = 1; nitems = 1; /* * Scan the format for conversions (`%' character). */ for (;;) { for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) /* void */; if (ch == '\0') goto done; fmt++; /* skip over '%' */ flags = 0; rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': case '#': goto rflag; case '*': ADDASTER (); goto rflag; case '-': 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 { n = 10 * n + to_digit(ch); ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { nextarg = n; goto rflag; } goto reswitch; #ifndef NO_FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': if (flags & SHORTINT) { flags &= ~SHORTINT; flags |= CHARINT; } else flags |= SHORTINT; goto rflag; case 'j': flags |= INTMAXT; goto rflag; case 'l': if (flags & LONGINT) { flags &= ~LONGINT; flags |= LLONGINT; } else flags |= LONGINT; goto rflag; case 'q': flags |= LLONGINT; /* not necessarily */ goto rflag; case 't': flags |= PTRDIFFT; goto rflag; case 'z': flags |= SIZET; goto rflag; case 'C': flags |= LONGINT; /*FALLTHROUGH*/ case 'c': if (flags & LONGINT) ADDTYPE(T_WINT); else ADDTYPE(T_INT); break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': ADDSARG(); break; #ifndef NO_FLOATING_POINT case 'a': case 'A': case 'e': case 'E': case 'f': case 'g': case 'G': if (flags & LONGDBL) ADDTYPE(T_LONG_DOUBLE); else ADDTYPE(T_DOUBLE); break; #endif /* !NO_FLOATING_POINT */ case 'n': if (flags & INTMAXT) ADDTYPE(TP_INTMAXT); else if (flags & PTRDIFFT) ADDTYPE(TP_PTRDIFFT); else if (flags & SIZET) ADDTYPE(TP_SIZET); else if (flags & LLONGINT) ADDTYPE(TP_LLONG); else if (flags & LONGINT) ADDTYPE(TP_LONG); else if (flags & SHORTINT) ADDTYPE(TP_SHORT); else if (flags & CHARINT) ADDTYPE(TP_SCHAR); else ADDTYPE(TP_INT); continue; /* no output */ case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': ADDUARG(); break; case 'p': ADDTYPE(TP_VOID); break; case 'S': flags |= LONGINT; /*FALLTHROUGH*/ case 's': if (flags & LONGINT) ADDTYPE(TP_WCHAR); else ADDTYPE(TP_CHAR); break; case 'U': flags |= LONGINT; /*FALLTHROUGH*/ case 'u': case 'X': case 'x': ADDUARG(); break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; break; } } done: /* * nitems contains the number of arguments we picked from the stack. * If tablemax is larger, this means that some positional argument, * tried to pick an argument the number of arguments possibly supplied. * Since positional arguments are typically used to swap the order of * the printf arguments and not to pick random arguments from strange * positions in the stack, we assume that if the positional argument * is trying to pick beyond the end of arguments, then this is wrong. * Alternatively we could find a way to figure out when va_arg() runs * out, but how to do that? */ if (nitems < tablemax) { if (typetable != stattypetable) free(typetable); return -1; } /* * Build the argument table. */ if (tablemax >= STATIC_ARG_TBL_SIZE) { *argtable = malloc(sizeof(**argtable) * (tablemax + 1)); if (*argtable == NULL) { free(typetable); return -1; } } (*argtable) [0].intarg = 0; for (n = 1; n <= tablemax; n++) { switch (typetable [n]) { case T_UNUSED: /* whoops! */ (*argtable) [n].intarg = va_arg (ap, int); break; case TP_SCHAR: (*argtable) [n].pschararg = va_arg (ap, signed char *); break; case TP_SHORT: (*argtable) [n].pshortarg = va_arg (ap, short *); break; case T_INT: (*argtable) [n].intarg = va_arg (ap, int); 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_PTRDIFFT: (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); break; case TP_PTRDIFFT: (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); break; case T_SSIZET: (*argtable) [n].ssizearg = va_arg (ap, ssize_t); break; case T_SIZET: (*argtable) [n].sizearg = va_arg (ap, size_t); break; case TP_SIZET: (*argtable) [n].psizearg = va_arg (ap, size_t *); break; case T_INTMAXT: (*argtable) [n].intmaxarg = va_arg (ap, intmax_t); break; case T_UINTMAXT: (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); break; case TP_INTMAXT: (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); break; case T_DOUBLE: #ifndef NO_FLOATING_POINT (*argtable) [n].doublearg = va_arg (ap, double); #endif break; case T_LONG_DOUBLE: #ifndef NO_FLOATING_POINT (*argtable) [n].longdoublearg = va_arg (ap, long double); #endif 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_WINT: (*argtable) [n].wintarg = va_arg (ap, wint_t); break; case TP_WCHAR: (*argtable) [n].pwchararg = va_arg (ap, wchar_t *); break; } } if (typetable != stattypetable) free (typetable); return 0; } /* * Increase the size of the type table. */ static int __grow_type_table (size_t nextarg, enum typeid **typetable, size_t *tablesize) { enum typeid *const oldtable = *typetable; const size_t oldsize = *tablesize; enum typeid *newtable; size_t newsize = oldsize * 2; if (newsize < nextarg + 1) newsize = nextarg + 1; if (oldsize == STATIC_ARG_TBL_SIZE) { newtable = NULL; errno = reallocarr(&newtable, newsize, sizeof(*newtable)); if (errno) return -1; memcpy(newtable, oldtable, oldsize * sizeof(*newtable)); } else { newtable = oldtable; errno = reallocarr(&newtable, newsize, sizeof(*newtable)); if (errno) { int serrno = errno; free(oldtable); errno = serrno; return -1; } } memset(&newtable[oldsize], 0, (newsize - oldsize) * sizeof(*newtable)); *typetable = newtable; *tablesize = newsize; return 0; } #ifndef NO_FLOATING_POINT static int exponent(CHAR_T *p0, int expo, int fmtch) { CHAR_T *p, *t; CHAR_T expbuf[MAXEXPDIG]; p = p0; *p++ = fmtch; if (expo < 0) { expo = -expo; *p++ = '-'; } else *p++ = '+'; t = expbuf + MAXEXPDIG; if (expo > 9) { do { *--t = to_char(expo % 10); } while ((expo /= 10) > 9); *--t = to_char(expo); for (; t < expbuf + MAXEXPDIG; *p++ = *t++); } 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(expo); } _DIAGASSERT(__type_fit(int, p - p0)); return (int)(p - p0); } #endif /* !NO_FLOATING_POINT */