vfprintf.c revision 128550
11573Srgrimes/*- 21573Srgrimes * Copyright (c) 1990, 1993 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * This code is derived from software contributed to Berkeley by 61573Srgrimes * Chris Torek. 71573Srgrimes * 81573Srgrimes * Redistribution and use in source and binary forms, with or without 91573Srgrimes * modification, are permitted provided that the following conditions 101573Srgrimes * are met: 111573Srgrimes * 1. Redistributions of source code must retain the above copyright 121573Srgrimes * notice, this list of conditions and the following disclaimer. 131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141573Srgrimes * notice, this list of conditions and the following disclaimer in the 151573Srgrimes * documentation and/or other materials provided with the distribution. 161573Srgrimes * 3. All advertising materials mentioning features or use of this software 171573Srgrimes * must display the following acknowledgement: 181573Srgrimes * This product includes software developed by the University of 191573Srgrimes * California, Berkeley and its contributors. 201573Srgrimes * 4. Neither the name of the University nor the names of its contributors 211573Srgrimes * may be used to endorse or promote products derived from this software 221573Srgrimes * without specific prior written permission. 231573Srgrimes * 241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341573Srgrimes * SUCH DAMAGE. 351573Srgrimes */ 361573Srgrimes 371573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 381573Srgrimesstatic char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; 391573Srgrimes#endif /* LIBC_SCCS and not lint */ 4092986Sobrien#include <sys/cdefs.h> 4192986Sobrien__FBSDID("$FreeBSD: head/lib/libc/stdio/vfprintf.c 128550 2004-04-22 11:35:12Z tjr $"); 421573Srgrimes 431573Srgrimes/* 441573Srgrimes * Actual printf innards. 451573Srgrimes * 461573Srgrimes * This code is large and complicated... 471573Srgrimes */ 481573Srgrimes 4971579Sdeischen#include "namespace.h" 501573Srgrimes#include <sys/types.h> 511573Srgrimes 5287113Sfenner#include <ctype.h> 531573Srgrimes#include <limits.h> 5487490Sphantom#include <locale.h> 5587113Sfenner#include <stddef.h> 5687113Sfenner#include <stdint.h> 571573Srgrimes#include <stdio.h> 581573Srgrimes#include <stdlib.h> 591573Srgrimes#include <string.h> 60103633Stjr#include <wchar.h> 611573Srgrimes 621573Srgrimes#include <stdarg.h> 6371579Sdeischen#include "un-namespace.h" 641573Srgrimes 6571579Sdeischen#include "libc_private.h" 661573Srgrimes#include "local.h" 671573Srgrimes#include "fvwrite.h" 681573Srgrimes 69124667Sdas/* Define FLOATING_POINT to get floating point, HEXFLOAT to get %a. */ 701573Srgrimes#define FLOATING_POINT 71124667Sdas#define HEXFLOAT 721573Srgrimes 7384922Sdfrunion arg { 7484962Sbde int intarg; 7584962Sbde u_int uintarg; 7684962Sbde long longarg; 7784962Sbde u_long ulongarg; 7887113Sfenner long long longlongarg; 7987113Sfenner unsigned long long ulonglongarg; 8087113Sfenner ptrdiff_t ptrdiffarg; 8187113Sfenner size_t sizearg; 8287113Sfenner intmax_t intmaxarg; 8387113Sfenner uintmax_t uintmaxarg; 8484962Sbde void *pvoidarg; 8584962Sbde char *pchararg; 8687113Sfenner signed char *pschararg; 8784962Sbde short *pshortarg; 8884962Sbde int *pintarg; 8984962Sbde long *plongarg; 9087113Sfenner long long *plonglongarg; 9187113Sfenner ptrdiff_t *pptrdiffarg; 9287113Sfenner size_t *psizearg; 9387113Sfenner intmax_t *pintmaxarg; 9484922Sdfr#ifdef FLOATING_POINT 9584962Sbde double doublearg; 9684962Sbde long double longdoublearg; 9784922Sdfr#endif 98103633Stjr wint_t wintarg; 99103633Stjr wchar_t *pwchararg; 10084922Sdfr}; 10184922Sdfr 10287113Sfenner/* 10387113Sfenner * Type ids for argument type table. 10487113Sfenner */ 10587113Sfennerenum typeid { 10687113Sfenner T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT, 10787113Sfenner T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, 10887113Sfenner T_PTRDIFFT, TP_PTRDIFFT, T_SIZET, TP_SIZET, 10987113Sfenner T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR, 110103633Stjr T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR 11187113Sfenner}; 11287113Sfenner 11392905Sobrienstatic int __sprint(FILE *, struct __suio *); 11492941Sobrienstatic int __sbprintf(FILE *, const char *, va_list) __printflike(2, 0); 115113146Sdasstatic char *__ujtoa(uintmax_t, char *, int, int, const char *, int, char, 11692941Sobrien const char *); 117113146Sdasstatic char *__ultoa(u_long, char *, int, int, const char *, int, char, 11892941Sobrien const char *); 119103633Stjrstatic char *__wcsconv(wchar_t *, int); 12092905Sobrienstatic void __find_arguments(const char *, va_list, union arg **); 12192905Sobrienstatic void __grow_type_table(int, enum typeid **, int *); 12216586Sjraynard 1231573Srgrimes/* 1241573Srgrimes * Flush out all the vectors defined by the given uio, 1251573Srgrimes * then reset it so that it can be reused. 1261573Srgrimes */ 1271573Srgrimesstatic int 12871579Sdeischen__sprint(FILE *fp, struct __suio *uio) 1291573Srgrimes{ 13071579Sdeischen int err; 1311573Srgrimes 1321573Srgrimes if (uio->uio_resid == 0) { 1331573Srgrimes uio->uio_iovcnt = 0; 1341573Srgrimes return (0); 1351573Srgrimes } 1361573Srgrimes err = __sfvwrite(fp, uio); 1371573Srgrimes uio->uio_resid = 0; 1381573Srgrimes uio->uio_iovcnt = 0; 1391573Srgrimes return (err); 1401573Srgrimes} 1411573Srgrimes 1421573Srgrimes/* 1431573Srgrimes * Helper function for `fprintf to unbuffered unix file': creates a 1441573Srgrimes * temporary buffer. We only work on write-only files; this avoids 1451573Srgrimes * worries about ungetc buffers and so forth. 1461573Srgrimes */ 1471573Srgrimesstatic int 14871579Sdeischen__sbprintf(FILE *fp, const char *fmt, va_list ap) 1491573Srgrimes{ 1501573Srgrimes int ret; 1511573Srgrimes FILE fake; 1521573Srgrimes unsigned char buf[BUFSIZ]; 1531573Srgrimes 1541573Srgrimes /* copy the important variables */ 1551573Srgrimes fake._flags = fp->_flags & ~__SNBF; 1561573Srgrimes fake._file = fp->_file; 1571573Srgrimes fake._cookie = fp->_cookie; 1581573Srgrimes fake._write = fp->_write; 159101776Stjr fake._extra = fp->_extra; 1601573Srgrimes 1611573Srgrimes /* set up the buffer */ 1621573Srgrimes fake._bf._base = fake._p = buf; 1631573Srgrimes fake._bf._size = fake._w = sizeof(buf); 1641573Srgrimes fake._lbfsize = 0; /* not actually used, but Just In Case */ 1651573Srgrimes 1661573Srgrimes /* do the work, then copy any error status */ 16771579Sdeischen ret = __vfprintf(&fake, fmt, ap); 16871579Sdeischen if (ret >= 0 && __fflush(&fake)) 1691573Srgrimes ret = EOF; 1701573Srgrimes if (fake._flags & __SERR) 1711573Srgrimes fp->_flags |= __SERR; 1721573Srgrimes return (ret); 1731573Srgrimes} 1741573Srgrimes 1751573Srgrimes/* 1761573Srgrimes * Macros for converting digits to letters and vice versa 1771573Srgrimes */ 1781573Srgrimes#define to_digit(c) ((c) - '0') 1791573Srgrimes#define is_digit(c) ((unsigned)to_digit(c) <= 9) 1801573Srgrimes#define to_char(n) ((n) + '0') 1811573Srgrimes 1821573Srgrimes/* 1831573Srgrimes * Convert an unsigned long to ASCII for printf purposes, returning 1841573Srgrimes * a pointer to the first character of the string representation. 1851573Srgrimes * Octal numbers can be forced to have a leading zero; hex numbers 1861573Srgrimes * use the given digits. 1871573Srgrimes */ 1881573Srgrimesstatic char * 189113146Sdas__ultoa(u_long val, char *endp, int base, int octzero, const char *xdigs, 19087815Sphantom int needgrp, char thousep, const char *grp) 1911573Srgrimes{ 19292889Sobrien char *cp = endp; 19392889Sobrien long sval; 19487113Sfenner int ndig; 1951573Srgrimes 1961573Srgrimes /* 1971573Srgrimes * Handle the three cases separately, in the hope of getting 1981573Srgrimes * better/faster code. 1991573Srgrimes */ 2001573Srgrimes switch (base) { 2011573Srgrimes case 10: 2021573Srgrimes if (val < 10) { /* many numbers are 1 digit */ 2031573Srgrimes *--cp = to_char(val); 2041573Srgrimes return (cp); 2051573Srgrimes } 20687113Sfenner ndig = 0; 2071573Srgrimes /* 2081573Srgrimes * On many machines, unsigned arithmetic is harder than 2091573Srgrimes * signed arithmetic, so we do at most one unsigned mod and 2101573Srgrimes * divide; this is sufficient to reduce the range of 2111573Srgrimes * the incoming value to where signed arithmetic works. 2121573Srgrimes */ 2131573Srgrimes if (val > LONG_MAX) { 2141573Srgrimes *--cp = to_char(val % 10); 21587113Sfenner ndig++; 2161573Srgrimes sval = val / 10; 2171573Srgrimes } else 2181573Srgrimes sval = val; 2191573Srgrimes do { 2201573Srgrimes *--cp = to_char(sval % 10); 22187815Sphantom ndig++; 22287815Sphantom /* 22387815Sphantom * If (*grp == CHAR_MAX) then no more grouping 22487815Sphantom * should be performed. 22587815Sphantom */ 22687818Sphantom if (needgrp && ndig == *grp && *grp != CHAR_MAX 22787818Sphantom && sval > 9) { 22887815Sphantom *--cp = thousep; 22987113Sfenner ndig = 0; 23087815Sphantom /* 23187815Sphantom * If (*(grp+1) == '\0') then we have to 23287815Sphantom * use *grp character (last grouping rule) 23387815Sphantom * for all next cases 23487815Sphantom */ 23588057Sphantom if (*(grp+1) != '\0') 23688057Sphantom grp++; 23787113Sfenner } 2381573Srgrimes sval /= 10; 2391573Srgrimes } while (sval != 0); 2401573Srgrimes break; 2411573Srgrimes 2421573Srgrimes case 8: 2431573Srgrimes do { 2441573Srgrimes *--cp = to_char(val & 7); 2451573Srgrimes val >>= 3; 2461573Srgrimes } while (val); 2471573Srgrimes if (octzero && *cp != '0') 2481573Srgrimes *--cp = '0'; 2491573Srgrimes break; 2501573Srgrimes 2511573Srgrimes case 16: 2521573Srgrimes do { 2531573Srgrimes *--cp = xdigs[val & 15]; 2541573Srgrimes val >>= 4; 2551573Srgrimes } while (val); 2561573Srgrimes break; 2571573Srgrimes 2581573Srgrimes default: /* oops */ 2591573Srgrimes abort(); 2601573Srgrimes } 2611573Srgrimes return (cp); 2621573Srgrimes} 2631573Srgrimes 26487113Sfenner/* Identical to __ultoa, but for intmax_t. */ 2651573Srgrimesstatic char * 266113146Sdas__ujtoa(uintmax_t val, char *endp, int base, int octzero, const char *xdigs, 26787815Sphantom int needgrp, char thousep, const char *grp) 2681573Srgrimes{ 26971579Sdeischen char *cp = endp; 27087113Sfenner intmax_t sval; 27187113Sfenner int ndig; 2721573Srgrimes 2731573Srgrimes /* quick test for small values; __ultoa is typically much faster */ 2741573Srgrimes /* (perhaps instead we should run until small, then call __ultoa?) */ 2751573Srgrimes if (val <= ULONG_MAX) 27687113Sfenner return (__ultoa((u_long)val, endp, base, octzero, xdigs, 27787815Sphantom needgrp, thousep, grp)); 2781573Srgrimes switch (base) { 2791573Srgrimes case 10: 2801573Srgrimes if (val < 10) { 2811573Srgrimes *--cp = to_char(val % 10); 2821573Srgrimes return (cp); 2831573Srgrimes } 28487113Sfenner ndig = 0; 28587113Sfenner if (val > INTMAX_MAX) { 2861573Srgrimes *--cp = to_char(val % 10); 28787113Sfenner ndig++; 2881573Srgrimes sval = val / 10; 2891573Srgrimes } else 2901573Srgrimes sval = val; 2911573Srgrimes do { 2921573Srgrimes *--cp = to_char(sval % 10); 29387815Sphantom ndig++; 29487815Sphantom /* 29587815Sphantom * If (*grp == CHAR_MAX) then no more grouping 29687815Sphantom * should be performed. 29787815Sphantom */ 29887818Sphantom if (needgrp && *grp != CHAR_MAX && ndig == *grp 29987818Sphantom && sval > 9) { 30087815Sphantom *--cp = thousep; 30187113Sfenner ndig = 0; 30287815Sphantom /* 30387815Sphantom * If (*(grp+1) == '\0') then we have to 30487815Sphantom * use *grp character (last grouping rule) 30587815Sphantom * for all next cases 30687815Sphantom */ 30788057Sphantom if (*(grp+1) != '\0') 30888057Sphantom grp++; 30988057Sphantom } 3101573Srgrimes sval /= 10; 3111573Srgrimes } while (sval != 0); 3121573Srgrimes break; 3131573Srgrimes 3141573Srgrimes case 8: 3151573Srgrimes do { 3161573Srgrimes *--cp = to_char(val & 7); 3171573Srgrimes val >>= 3; 3181573Srgrimes } while (val); 3191573Srgrimes if (octzero && *cp != '0') 3201573Srgrimes *--cp = '0'; 3211573Srgrimes break; 3221573Srgrimes 3231573Srgrimes case 16: 3241573Srgrimes do { 3251573Srgrimes *--cp = xdigs[val & 15]; 3261573Srgrimes val >>= 4; 3271573Srgrimes } while (val); 3281573Srgrimes break; 3291573Srgrimes 3301573Srgrimes default: 3311573Srgrimes abort(); 3321573Srgrimes } 3331573Srgrimes return (cp); 3341573Srgrimes} 3351573Srgrimes 33671579Sdeischen/* 337103633Stjr * Convert a wide character string argument for the %ls format to a multibyte 338103633Stjr * string representation. ``prec'' specifies the maximum number of bytes 339103633Stjr * to output. If ``prec'' is greater than or equal to zero, we can't assume 340103633Stjr * that the wide char. string ends in a null character. 341103633Stjr */ 342103633Stjrstatic char * 343103633Stjr__wcsconv(wchar_t *wcsarg, int prec) 344103633Stjr{ 345128002Stjr static const mbstate_t initial; 346128002Stjr mbstate_t mbs; 347103633Stjr char buf[MB_LEN_MAX]; 348103633Stjr wchar_t *p; 349103633Stjr char *convbuf, *mbp; 350103633Stjr size_t clen, nbytes; 351103633Stjr 352103633Stjr /* 353103633Stjr * Determine the number of bytes to output and allocate space for 354103633Stjr * the output. 355103633Stjr */ 356103633Stjr if (prec >= 0) { 357103633Stjr nbytes = 0; 358103633Stjr p = wcsarg; 359128002Stjr mbs = initial; 360103633Stjr for (;;) { 361128002Stjr clen = wcrtomb(buf, *p++, &mbs); 362103633Stjr if (clen == 0 || clen == (size_t)-1 || 363103633Stjr nbytes + clen > prec) 364103633Stjr break; 365103633Stjr nbytes += clen; 366103633Stjr } 367103633Stjr } else { 368103633Stjr p = wcsarg; 369128002Stjr mbs = initial; 370128002Stjr nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); 371103633Stjr if (nbytes == (size_t)-1) 372103633Stjr return (NULL); 373103633Stjr } 374103633Stjr if ((convbuf = malloc(nbytes + 1)) == NULL) 375103633Stjr return (NULL); 376103633Stjr 377103633Stjr /* 378103633Stjr * Fill the output buffer with the multibyte representations of as 379103633Stjr * many wide characters as will fit. 380103633Stjr */ 381103633Stjr mbp = convbuf; 382103633Stjr p = wcsarg; 383128002Stjr mbs = initial; 384103633Stjr while (mbp - convbuf < nbytes) { 385128002Stjr clen = wcrtomb(mbp, *p++, &mbs); 386103633Stjr if (clen == 0 || clen == (size_t)-1) 387103633Stjr break; 388103633Stjr mbp += clen; 389103633Stjr } 390113196Sache if (clen == (size_t)-1) { 391113196Sache free(convbuf); 392113196Sache return (NULL); 393113196Sache } 394103633Stjr *mbp = '\0'; 395103633Stjr 396103633Stjr return (convbuf); 397103633Stjr} 398103633Stjr 399103633Stjr/* 40071579Sdeischen * MT-safe version 40171579Sdeischen */ 40271579Sdeischenint 403103012Stjrvfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap) 404101914Srobert 40571579Sdeischen{ 40671579Sdeischen int ret; 40771579Sdeischen 40871579Sdeischen FLOCKFILE(fp); 40971579Sdeischen ret = __vfprintf(fp, fmt0, ap); 41071579Sdeischen FUNLOCKFILE(fp); 41171579Sdeischen return (ret); 41271579Sdeischen} 41371579Sdeischen 4141573Srgrimes#ifdef FLOATING_POINT 415113146Sdas 416113146Sdas#define dtoa __dtoa 417113146Sdas#define freedtoa __freedtoa 418113146Sdas 419113146Sdas#include <float.h> 4201573Srgrimes#include <math.h> 4211573Srgrimes#include "floatio.h" 422113146Sdas#include "gdtoa.h" 4231573Srgrimes 4241573Srgrimes#define DEFPREC 6 4251573Srgrimes 42692905Sobrienstatic int exponent(char *, int, int); 4271573Srgrimes 428113142Sdas#endif /* FLOATING_POINT */ 4291573Srgrimes 430113142Sdas/* 431113142Sdas * The size of the buffer we use as scratch space for integer 432113142Sdas * conversions, among other things. Technically, we would need the 433113142Sdas * most space for base 10 conversions with thousands' grouping 434113142Sdas * characters between each pair of digits. 100 bytes is a 435113142Sdas * conservative overestimate even for a 128-bit uintmax_t. 436113142Sdas */ 437113142Sdas#define BUF 100 4381573Srgrimes 43921674Sjkh#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ 4401573Srgrimes 4411573Srgrimes/* 4421573Srgrimes * Flags used during conversion. 4431573Srgrimes */ 4441573Srgrimes#define ALT 0x001 /* alternate form */ 4451573Srgrimes#define LADJUST 0x004 /* left adjustment */ 44631871Sbde#define LONGDBL 0x008 /* long double */ 4471573Srgrimes#define LONGINT 0x010 /* long integer */ 44887113Sfenner#define LLONGINT 0x020 /* long long integer */ 4491573Srgrimes#define SHORTINT 0x040 /* short integer */ 4501573Srgrimes#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ 45188057Sphantom#define FPT 0x100 /* Floating point number */ 45287815Sphantom#define GROUPING 0x200 /* use grouping ("'" flag) */ 45387113Sfenner /* C99 additional size modifiers: */ 45487815Sphantom#define SIZET 0x400 /* size_t */ 45587815Sphantom#define PTRDIFFT 0x800 /* ptrdiff_t */ 45687815Sphantom#define INTMAXT 0x1000 /* intmax_t */ 45787815Sphantom#define CHARINT 0x2000 /* print char using int format */ 45887113Sfenner 45971579Sdeischen/* 46071579Sdeischen * Non-MT-safe version 46171579Sdeischen */ 4621573Srgrimesint 46371579Sdeischen__vfprintf(FILE *fp, const char *fmt0, va_list ap) 4641573Srgrimes{ 46571579Sdeischen char *fmt; /* format string */ 46671579Sdeischen int ch; /* character from fmt */ 46771579Sdeischen int n, n2; /* handy integer (short term usage) */ 46871579Sdeischen char *cp; /* handy char pointer (short term usage) */ 46971579Sdeischen struct __siov *iovp; /* for PRINT macro */ 47071579Sdeischen int flags; /* flags as above */ 4711573Srgrimes int ret; /* return value accumulator */ 4721573Srgrimes int width; /* width from format (%8d), or 0 */ 473113146Sdas int prec; /* precision from format; <0 for N/A */ 4741573Srgrimes char sign; /* sign prefix (' ', '+', '-', or \0) */ 47587815Sphantom char thousands_sep; /* locale specific thousands separator */ 47687815Sphantom const char *grouping; /* locale specific numeric grouping rules */ 4771573Srgrimes#ifdef FLOATING_POINT 478113146Sdas /* 479113146Sdas * We can decompose the printed representation of floating 480113146Sdas * point numbers into several parts, some of which may be empty: 481113146Sdas * 482113146Sdas * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ 483113146Sdas * A B ---C--- D E F 484113146Sdas * 485113146Sdas * A: 'sign' holds this value if present; '\0' otherwise 486113146Sdas * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal 487113146Sdas * C: cp points to the string MMMNNN. Leading and trailing 488113146Sdas * zeros are not in the string and must be added. 489113146Sdas * D: expchar holds this character; '\0' if no exponent, e.g. %f 490113146Sdas * F: at least two digits for decimal, at least one digit for hex 491113146Sdas */ 49287490Sphantom char *decimal_point; /* locale specific decimal point */ 493113146Sdas int signflag; /* true if float is negative */ 494113146Sdas union { /* floating point arguments %[aAeEfFgG] */ 495113146Sdas double dbl; 496113146Sdas long double ldbl; 497113146Sdas } fparg; 4981573Srgrimes int expt; /* integer value of exponent */ 499113146Sdas char expchar; /* exponent character: [eEpP\0] */ 500113146Sdas char *dtoaend; /* pointer to end of converted digits */ 5011573Srgrimes int expsize; /* character count for expstr */ 502113146Sdas int lead; /* sig figs before decimal or group sep */ 503113146Sdas int ndig; /* actual number of digits returned by dtoa */ 504113146Sdas char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ 50572523Stegge char *dtoaresult; /* buffer allocated by dtoa */ 506113146Sdas int nseps; /* number of group separators with ' */ 507113146Sdas int nrepeats; /* number of repeats of the last group */ 5081573Srgrimes#endif 5091573Srgrimes u_long ulval; /* integer arguments %[diouxX] */ 51087113Sfenner uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ 5111573Srgrimes int base; /* base for [diouxX] conversion */ 5121573Srgrimes int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 51314727Sfenner int realsz; /* field size expanded by dprec, sign, etc */ 5141573Srgrimes int size; /* size of converted field or string */ 51531983Sache int prsize; /* max size of printed field */ 516113146Sdas const char *xdigs; /* digits for %[xX] conversion */ 5171573Srgrimes#define NIOV 8 5181573Srgrimes struct __suio uio; /* output information: summary */ 5191573Srgrimes struct __siov iov[NIOV];/* ... and individual io vectors */ 520113142Sdas char buf[BUF]; /* buffer with space for digits of uintmax_t */ 521113146Sdas char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ 522103399Stjr union arg *argtable; /* args, built due to positional arg */ 523103399Stjr union arg statargtable [STATIC_ARG_TBL_SIZE]; 524103399Stjr int nextarg; /* 1-based argument index */ 525103399Stjr va_list orgap; /* original argument pointer */ 526103633Stjr char *convbuf; /* wide to multibyte conversion result */ 5271573Srgrimes 5281573Srgrimes /* 5291573Srgrimes * Choose PADSIZE to trade efficiency vs. size. If larger printf 5301573Srgrimes * fields occur frequently, increase PADSIZE and make the initialisers 5311573Srgrimes * below longer. 5321573Srgrimes */ 5331573Srgrimes#define PADSIZE 16 /* pad chunk size */ 5341573Srgrimes static char blanks[PADSIZE] = 5351573Srgrimes {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 5361573Srgrimes static char zeroes[PADSIZE] = 5371573Srgrimes {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 5381573Srgrimes 539113146Sdas static const char xdigs_lower[16] = "0123456789abcdef"; 540113146Sdas static const char xdigs_upper[16] = "0123456789ABCDEF"; 541113146Sdas 5421573Srgrimes /* 5431573Srgrimes * BEWARE, these `goto error' on error, and PAD uses `n'. 5441573Srgrimes */ 5451573Srgrimes#define PRINT(ptr, len) { \ 5461573Srgrimes iovp->iov_base = (ptr); \ 5471573Srgrimes iovp->iov_len = (len); \ 5481573Srgrimes uio.uio_resid += (len); \ 5491573Srgrimes iovp++; \ 5501573Srgrimes if (++uio.uio_iovcnt >= NIOV) { \ 5511573Srgrimes if (__sprint(fp, &uio)) \ 5521573Srgrimes goto error; \ 5531573Srgrimes iovp = iov; \ 5541573Srgrimes } \ 5551573Srgrimes} 5561573Srgrimes#define PAD(howmany, with) { \ 5571573Srgrimes if ((n = (howmany)) > 0) { \ 5581573Srgrimes while (n > PADSIZE) { \ 5591573Srgrimes PRINT(with, PADSIZE); \ 5601573Srgrimes n -= PADSIZE; \ 5611573Srgrimes } \ 5621573Srgrimes PRINT(with, n); \ 5631573Srgrimes } \ 5641573Srgrimes} 565113191Sdas#define PRINTANDPAD(p, ep, len, with) do { \ 566113191Sdas n2 = (ep) - (p); \ 567113191Sdas if (n2 > (len)) \ 568113191Sdas n2 = (len); \ 569113191Sdas if (n2 > 0) \ 570113191Sdas PRINT((p), n2); \ 571113191Sdas PAD((len) - (n2 > 0 ? n2 : 0), (with)); \ 572113191Sdas} while(0) 5731573Srgrimes#define FLUSH() { \ 5741573Srgrimes if (uio.uio_resid && __sprint(fp, &uio)) \ 5751573Srgrimes goto error; \ 5761573Srgrimes uio.uio_iovcnt = 0; \ 5771573Srgrimes iovp = iov; \ 5781573Srgrimes} 5791573Srgrimes 580103399Stjr /* 581103399Stjr * Get the argument indexed by nextarg. If the argument table is 582103399Stjr * built, use it to get the argument. If its not, get the next 583103399Stjr * argument (and arguments must be gotten sequentially). 584103399Stjr */ 58521674Sjkh#define GETARG(type) \ 586103399Stjr ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ 587103399Stjr (nextarg++, va_arg(ap, type))) 58821674Sjkh 5891573Srgrimes /* 5901573Srgrimes * To extend shorts properly, we need both signed and unsigned 5911573Srgrimes * argument extraction methods. 5921573Srgrimes */ 5931573Srgrimes#define SARG() \ 59421674Sjkh (flags&LONGINT ? GETARG(long) : \ 59521674Sjkh flags&SHORTINT ? (long)(short)GETARG(int) : \ 59687113Sfenner flags&CHARINT ? (long)(signed char)GETARG(int) : \ 59721674Sjkh (long)GETARG(int)) 5981573Srgrimes#define UARG() \ 59921674Sjkh (flags&LONGINT ? GETARG(u_long) : \ 60021674Sjkh flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 60187113Sfenner flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ 60221674Sjkh (u_long)GETARG(u_int)) 60387113Sfenner#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) 60487113Sfenner#define SJARG() \ 60587113Sfenner (flags&INTMAXT ? GETARG(intmax_t) : \ 60687113Sfenner flags&SIZET ? (intmax_t)GETARG(size_t) : \ 60787113Sfenner flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ 60887113Sfenner (intmax_t)GETARG(long long)) 60987113Sfenner#define UJARG() \ 61087113Sfenner (flags&INTMAXT ? GETARG(uintmax_t) : \ 61187113Sfenner flags&SIZET ? (uintmax_t)GETARG(size_t) : \ 61287113Sfenner flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ 61387113Sfenner (uintmax_t)GETARG(unsigned long long)) 6141573Srgrimes 615103399Stjr /* 616103399Stjr * Get * arguments, including the form *nn$. Preserve the nextarg 617103399Stjr * that the argument can be gotten once the type is determined. 618103399Stjr */ 61921674Sjkh#define GETASTER(val) \ 620103399Stjr n2 = 0; \ 621103399Stjr cp = fmt; \ 622103399Stjr while (is_digit(*cp)) { \ 623103399Stjr n2 = 10 * n2 + to_digit(*cp); \ 624103399Stjr cp++; \ 625103399Stjr } \ 626103399Stjr if (*cp == '$') { \ 627103399Stjr int hold = nextarg; \ 628103399Stjr if (argtable == NULL) { \ 629103399Stjr argtable = statargtable; \ 630103399Stjr __find_arguments (fmt0, orgap, &argtable); \ 631103399Stjr } \ 632103399Stjr nextarg = n2; \ 63321674Sjkh val = GETARG (int); \ 634103399Stjr nextarg = hold; \ 635103399Stjr fmt = ++cp; \ 636103399Stjr } else { \ 637103399Stjr val = GETARG (int); \ 638103399Stjr } 63987815Sphantom 64088057Sphantom 64187815Sphantom thousands_sep = '\0'; 64287815Sphantom grouping = NULL; 643103633Stjr convbuf = NULL; 64472523Stegge#ifdef FLOATING_POINT 64572523Stegge dtoaresult = NULL; 64687113Sfenner decimal_point = localeconv()->decimal_point; 64772523Stegge#endif 6481573Srgrimes /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 64971579Sdeischen if (cantwrite(fp)) 6501573Srgrimes return (EOF); 6511573Srgrimes 6521573Srgrimes /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 6531573Srgrimes if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 65471579Sdeischen fp->_file >= 0) 6551573Srgrimes return (__sbprintf(fp, fmt0, ap)); 6561573Srgrimes 6571573Srgrimes fmt = (char *)fmt0; 658103399Stjr argtable = NULL; 659103399Stjr nextarg = 1; 660103876Stjr va_copy(orgap, ap); 6611573Srgrimes uio.uio_iov = iovp = iov; 6621573Srgrimes uio.uio_resid = 0; 6631573Srgrimes uio.uio_iovcnt = 0; 6641573Srgrimes ret = 0; 6651573Srgrimes 6661573Srgrimes /* 6671573Srgrimes * Scan the format for conversions (`%' character). 6681573Srgrimes */ 6691573Srgrimes for (;;) { 6701573Srgrimes for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 6711573Srgrimes /* void */; 6721573Srgrimes if ((n = fmt - cp) != 0) { 67332253Sache if ((unsigned)ret + n > INT_MAX) { 67431983Sache ret = EOF; 67531983Sache goto error; 67631983Sache } 6771573Srgrimes PRINT(cp, n); 6781573Srgrimes ret += n; 6791573Srgrimes } 6801573Srgrimes if (ch == '\0') 6811573Srgrimes goto done; 6821573Srgrimes fmt++; /* skip over '%' */ 6831573Srgrimes 6841573Srgrimes flags = 0; 6851573Srgrimes dprec = 0; 6861573Srgrimes width = 0; 6871573Srgrimes prec = -1; 6881573Srgrimes sign = '\0'; 689113146Sdas ox[1] = '\0'; 6901573Srgrimes 6911573Srgrimesrflag: ch = *fmt++; 6921573Srgrimesreswitch: switch (ch) { 6931573Srgrimes case ' ': 69488057Sphantom /*- 6951573Srgrimes * ``If the space and + flags both appear, the space 6961573Srgrimes * flag will be ignored.'' 6971573Srgrimes * -- ANSI X3J11 6981573Srgrimes */ 6991573Srgrimes if (!sign) 7001573Srgrimes sign = ' '; 7011573Srgrimes goto rflag; 7021573Srgrimes case '#': 7031573Srgrimes flags |= ALT; 7041573Srgrimes goto rflag; 7051573Srgrimes case '*': 70688057Sphantom /*- 7071573Srgrimes * ``A negative field width argument is taken as a 7081573Srgrimes * - flag followed by a positive field width.'' 7091573Srgrimes * -- ANSI X3J11 7101573Srgrimes * They don't exclude field widths read from args. 7111573Srgrimes */ 71221674Sjkh GETASTER (width); 71321674Sjkh if (width >= 0) 7141573Srgrimes goto rflag; 7151573Srgrimes width = -width; 7161573Srgrimes /* FALLTHROUGH */ 7171573Srgrimes case '-': 7181573Srgrimes flags |= LADJUST; 7191573Srgrimes goto rflag; 7201573Srgrimes case '+': 7211573Srgrimes sign = '+'; 7221573Srgrimes goto rflag; 72387113Sfenner case '\'': 72487815Sphantom flags |= GROUPING; 72587815Sphantom thousands_sep = *(localeconv()->thousands_sep); 72687815Sphantom grouping = localeconv()->grouping; 72787113Sfenner goto rflag; 7281573Srgrimes case '.': 7291573Srgrimes if ((ch = *fmt++) == '*') { 730113191Sdas GETASTER (prec); 7311573Srgrimes goto rflag; 7321573Srgrimes } 733113191Sdas prec = 0; 7341573Srgrimes while (is_digit(ch)) { 735113191Sdas prec = 10 * prec + to_digit(ch); 7361573Srgrimes ch = *fmt++; 7371573Srgrimes } 7381573Srgrimes goto reswitch; 7391573Srgrimes case '0': 74088057Sphantom /*- 7411573Srgrimes * ``Note that 0 is taken as a flag, not as the 7421573Srgrimes * beginning of a field width.'' 7431573Srgrimes * -- ANSI X3J11 7441573Srgrimes */ 7451573Srgrimes flags |= ZEROPAD; 7461573Srgrimes goto rflag; 7471573Srgrimes case '1': case '2': case '3': case '4': 7481573Srgrimes case '5': case '6': case '7': case '8': case '9': 7491573Srgrimes n = 0; 7501573Srgrimes do { 7511573Srgrimes n = 10 * n + to_digit(ch); 7521573Srgrimes ch = *fmt++; 7531573Srgrimes } while (is_digit(ch)); 75421674Sjkh if (ch == '$') { 75521674Sjkh nextarg = n; 756103399Stjr if (argtable == NULL) { 757103399Stjr argtable = statargtable; 758103399Stjr __find_arguments (fmt0, orgap, 759103399Stjr &argtable); 76021674Sjkh } 76121674Sjkh goto rflag; 762103399Stjr } 7631573Srgrimes width = n; 7641573Srgrimes goto reswitch; 7651573Srgrimes#ifdef FLOATING_POINT 7661573Srgrimes case 'L': 7671573Srgrimes flags |= LONGDBL; 7681573Srgrimes goto rflag; 7691573Srgrimes#endif 7701573Srgrimes case 'h': 77187113Sfenner if (flags & SHORTINT) { 77287113Sfenner flags &= ~SHORTINT; 77387113Sfenner flags |= CHARINT; 77487113Sfenner } else 77587113Sfenner flags |= SHORTINT; 7761573Srgrimes goto rflag; 77787113Sfenner case 'j': 77887113Sfenner flags |= INTMAXT; 77987113Sfenner goto rflag; 7801573Srgrimes case 'l': 78187113Sfenner if (flags & LONGINT) { 78287113Sfenner flags &= ~LONGINT; 78387113Sfenner flags |= LLONGINT; 78487113Sfenner } else 78544674Sdfr flags |= LONGINT; 7861573Srgrimes goto rflag; 7871573Srgrimes case 'q': 78887113Sfenner flags |= LLONGINT; /* not necessarily */ 7891573Srgrimes goto rflag; 79087113Sfenner case 't': 79187113Sfenner flags |= PTRDIFFT; 79287113Sfenner goto rflag; 79387113Sfenner case 'z': 79487113Sfenner flags |= SIZET; 79587113Sfenner goto rflag; 796105204Stjr case 'C': 797105204Stjr flags |= LONGINT; 798105204Stjr /*FALLTHROUGH*/ 7991573Srgrimes case 'c': 800103633Stjr if (flags & LONGINT) { 801128002Stjr static const mbstate_t initial; 802128002Stjr mbstate_t mbs; 803103633Stjr size_t mbseqlen; 804103633Stjr 805128002Stjr mbs = initial; 806103633Stjr mbseqlen = wcrtomb(cp = buf, 807128002Stjr (wchar_t)GETARG(wint_t), &mbs); 808105234Stjr if (mbseqlen == (size_t)-1) { 809105234Stjr fp->_flags |= __SERR; 810103633Stjr goto error; 811105234Stjr } 812103633Stjr size = (int)mbseqlen; 813103633Stjr } else { 814103633Stjr *(cp = buf) = GETARG(int); 815103633Stjr size = 1; 816103633Stjr } 8171573Srgrimes sign = '\0'; 8181573Srgrimes break; 8191573Srgrimes case 'D': 8201573Srgrimes flags |= LONGINT; 8211573Srgrimes /*FALLTHROUGH*/ 8221573Srgrimes case 'd': 8231573Srgrimes case 'i': 82487113Sfenner if (flags & INTMAX_SIZE) { 82587113Sfenner ujval = SJARG(); 82687113Sfenner if ((intmax_t)ujval < 0) { 82787113Sfenner ujval = -ujval; 8281573Srgrimes sign = '-'; 8291573Srgrimes } 8301573Srgrimes } else { 8311573Srgrimes ulval = SARG(); 8321573Srgrimes if ((long)ulval < 0) { 8331573Srgrimes ulval = -ulval; 8341573Srgrimes sign = '-'; 8351573Srgrimes } 8361573Srgrimes } 8371573Srgrimes base = 10; 8381573Srgrimes goto number; 8391573Srgrimes#ifdef FLOATING_POINT 84087113Sfenner#ifdef HEXFLOAT 84187113Sfenner case 'a': 84287113Sfenner case 'A': 843113146Sdas if (ch == 'a') { 844113146Sdas ox[1] = 'x'; 845113146Sdas xdigs = xdigs_lower; 846113146Sdas expchar = 'p'; 847113146Sdas } else { 848113146Sdas ox[1] = 'X'; 849113146Sdas xdigs = xdigs_upper; 850113146Sdas expchar = 'P'; 851113146Sdas } 852124657Sdas if (prec >= 0) 853124657Sdas prec++; 854124657Sdas if (dtoaresult != NULL) 855124657Sdas freedtoa(dtoaresult); 856113146Sdas if (flags & LONGDBL) { 857124657Sdas fparg.ldbl = GETARG(long double); 858113146Sdas dtoaresult = cp = 859113146Sdas __hldtoa(fparg.ldbl, xdigs, prec, 860113146Sdas &expt, &signflag, &dtoaend); 861113146Sdas } else { 862113146Sdas fparg.dbl = GETARG(double); 863113146Sdas dtoaresult = cp = 864113146Sdas __hdtoa(fparg.dbl, xdigs, prec, 865113146Sdas &expt, &signflag, &dtoaend); 866113146Sdas } 867124657Sdas if (prec < 0) 868124657Sdas prec = dtoaend - cp; 869124657Sdas if (expt == INT_MAX) 870124657Sdas ox[1] = '\0'; 871124657Sdas goto fp_common; 872124657Sdas#endif /* HEXFLOAT */ 8737033Sbde case 'e': 8741573Srgrimes case 'E': 875113146Sdas expchar = ch; 876113146Sdas if (prec < 0) /* account for digit before decpt */ 877113146Sdas prec = DEFPREC + 1; 878113146Sdas else 879113146Sdas prec++; 880113146Sdas goto fp_begin; 8817033Sbde case 'f': 88287113Sfenner case 'F': 883113146Sdas expchar = '\0'; 8847033Sbde goto fp_begin; 8851573Srgrimes case 'g': 8861573Srgrimes case 'G': 887113146Sdas expchar = ch - ('g' - 'e'); 8887033Sbde if (prec == 0) 8897033Sbde prec = 1; 890113146Sdasfp_begin: 891113146Sdas if (prec < 0) 8921573Srgrimes prec = DEFPREC; 893113146Sdas if (dtoaresult != NULL) 894113146Sdas freedtoa(dtoaresult); 895113146Sdas if (flags & LONGDBL) { 896113146Sdas fparg.ldbl = GETARG(long double); 897113146Sdas dtoaresult = cp = 898113146Sdas __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, 899113146Sdas &expt, &signflag, &dtoaend); 900113146Sdas } else { 901113146Sdas fparg.dbl = GETARG(double); 902113146Sdas dtoaresult = cp = 903113146Sdas dtoa(fparg.dbl, expchar ? 2 : 3, prec, 904113146Sdas &expt, &signflag, &dtoaend); 905113146Sdas if (expt == 9999) 906113146Sdas expt = INT_MAX; 9071573Srgrimes } 908124657Sdasfp_common: 909113146Sdas if (signflag) 910113146Sdas sign = '-'; 911113146Sdas if (expt == INT_MAX) { /* inf or nan */ 912113146Sdas if (*cp == 'N') { 913113146Sdas cp = (ch >= 'a') ? "nan" : "NAN"; 914113146Sdas sign = '\0'; 915113146Sdas } else 916113146Sdas cp = (ch >= 'a') ? "inf" : "INF"; 9171573Srgrimes size = 3; 9181573Srgrimes break; 9191573Srgrimes } 9201573Srgrimes flags |= FPT; 921113146Sdas ndig = dtoaend - cp; 9221573Srgrimes if (ch == 'g' || ch == 'G') { 923113146Sdas if (expt > -4 && expt <= prec) { 924113146Sdas /* Make %[gG] smell like %[fF] */ 925113146Sdas expchar = '\0'; 926113146Sdas if (flags & ALT) 927113146Sdas prec -= expt; 928113146Sdas else 929113146Sdas prec = ndig - expt; 930113146Sdas if (prec < 0) 931113146Sdas prec = 0; 932113723Sdas } else { 933113723Sdas /* 934113723Sdas * Make %[gG] smell like %[eE], but 935113723Sdas * trim trailing zeroes if no # flag. 936113723Sdas */ 937113723Sdas if (!(flags & ALT)) 938113723Sdas prec = ndig; 939113146Sdas } 9408870Srgrimes } 941113146Sdas if (expchar) { 942113146Sdas expsize = exponent(expstr, expt - 1, expchar); 943113146Sdas size = expsize + prec; 944113191Sdas if (prec > 1 || flags & ALT) 9451573Srgrimes ++size; 946113146Sdas } else { 947113468Sdas /* space for digits before decimal point */ 948113468Sdas if (expt > 0) 9491573Srgrimes size = expt; 950113468Sdas else /* "0" */ 951113468Sdas size = 1; 952113468Sdas /* space for decimal pt and following digits */ 953113468Sdas if (prec || flags & ALT) 954113468Sdas size += prec + 1; 955113146Sdas if (grouping && expt > 0) { 956113146Sdas /* space for thousands' grouping */ 957113146Sdas nseps = nrepeats = 0; 958113146Sdas lead = expt; 959113146Sdas while (*grouping != CHAR_MAX) { 960113146Sdas if (lead <= *grouping) 961113146Sdas break; 962113146Sdas lead -= *grouping; 963113146Sdas if (*(grouping+1)) { 964113146Sdas nseps++; 965113146Sdas grouping++; 966113146Sdas } else 967113146Sdas nrepeats++; 968113146Sdas } 969113146Sdas size += nseps + nrepeats; 970113146Sdas } else 971113194Sdas lead = expt; 972113146Sdas } 9731573Srgrimes break; 9741573Srgrimes#endif /* FLOATING_POINT */ 9751573Srgrimes case 'n': 97687113Sfenner /* 97787113Sfenner * Assignment-like behavior is specified if the 97887113Sfenner * value overflows or is otherwise unrepresentable. 97987113Sfenner * C99 says to use `signed char' for %hhn conversions. 98087113Sfenner */ 98187113Sfenner if (flags & LLONGINT) 98287113Sfenner *GETARG(long long *) = ret; 98387113Sfenner else if (flags & SIZET) 98487113Sfenner *GETARG(ssize_t *) = (ssize_t)ret; 98587113Sfenner else if (flags & PTRDIFFT) 98687113Sfenner *GETARG(ptrdiff_t *) = ret; 98787113Sfenner else if (flags & INTMAXT) 98887113Sfenner *GETARG(intmax_t *) = ret; 9891573Srgrimes else if (flags & LONGINT) 99031980Sache *GETARG(long *) = ret; 9911573Srgrimes else if (flags & SHORTINT) 99231980Sache *GETARG(short *) = ret; 99387113Sfenner else if (flags & CHARINT) 99487113Sfenner *GETARG(signed char *) = ret; 9951573Srgrimes else 99631980Sache *GETARG(int *) = ret; 9971573Srgrimes continue; /* no output */ 9981573Srgrimes case 'O': 9991573Srgrimes flags |= LONGINT; 10001573Srgrimes /*FALLTHROUGH*/ 10011573Srgrimes case 'o': 100287113Sfenner if (flags & INTMAX_SIZE) 100387113Sfenner ujval = UJARG(); 10041573Srgrimes else 10051573Srgrimes ulval = UARG(); 10061573Srgrimes base = 8; 10071573Srgrimes goto nosign; 10081573Srgrimes case 'p': 100988057Sphantom /*- 10101573Srgrimes * ``The argument shall be a pointer to void. The 10111573Srgrimes * value of the pointer is converted to a sequence 10121573Srgrimes * of printable characters, in an implementation- 10131573Srgrimes * defined manner.'' 10141573Srgrimes * -- ANSI X3J11 10151573Srgrimes */ 101687113Sfenner ujval = (uintmax_t)(uintptr_t)GETARG(void *); 10171573Srgrimes base = 16; 1018113146Sdas xdigs = xdigs_lower; 1019113146Sdas flags = flags | INTMAXT; 1020113146Sdas ox[1] = 'x'; 10211573Srgrimes goto nosign; 1022105204Stjr case 'S': 1023105204Stjr flags |= LONGINT; 1024105204Stjr /*FALLTHROUGH*/ 10251573Srgrimes case 's': 1026103633Stjr if (flags & LONGINT) { 1027103633Stjr wchar_t *wcp; 1028103633Stjr 1029103633Stjr if (convbuf != NULL) 1030103633Stjr free(convbuf); 1031103633Stjr if ((wcp = GETARG(wchar_t *)) == NULL) 1032103633Stjr cp = "(null)"; 1033103633Stjr else { 1034103633Stjr convbuf = __wcsconv(wcp, prec); 1035105234Stjr if (convbuf == NULL) { 1036105234Stjr fp->_flags |= __SERR; 1037103633Stjr goto error; 1038105234Stjr } 1039103633Stjr cp = convbuf; 1040103633Stjr } 1041103633Stjr } else if ((cp = GETARG(char *)) == NULL) 10421573Srgrimes cp = "(null)"; 10431573Srgrimes if (prec >= 0) { 10441573Srgrimes /* 10451573Srgrimes * can't use strlen; can only look for the 10461573Srgrimes * NUL in the first `prec' characters, and 10471573Srgrimes * strlen() will go further. 10481573Srgrimes */ 104916586Sjraynard char *p = memchr(cp, 0, (size_t)prec); 10501573Srgrimes 10511573Srgrimes if (p != NULL) { 10521573Srgrimes size = p - cp; 10531573Srgrimes if (size > prec) 10541573Srgrimes size = prec; 10551573Srgrimes } else 10561573Srgrimes size = prec; 10571573Srgrimes } else 10581573Srgrimes size = strlen(cp); 10591573Srgrimes sign = '\0'; 10601573Srgrimes break; 10611573Srgrimes case 'U': 10621573Srgrimes flags |= LONGINT; 10631573Srgrimes /*FALLTHROUGH*/ 10641573Srgrimes case 'u': 106587113Sfenner if (flags & INTMAX_SIZE) 106687113Sfenner ujval = UJARG(); 10671573Srgrimes else 10681573Srgrimes ulval = UARG(); 10691573Srgrimes base = 10; 10701573Srgrimes goto nosign; 10711573Srgrimes case 'X': 1072113146Sdas xdigs = xdigs_upper; 10731573Srgrimes goto hex; 10741573Srgrimes case 'x': 1075113146Sdas xdigs = xdigs_lower; 107687113Sfennerhex: 107787113Sfenner if (flags & INTMAX_SIZE) 107887113Sfenner ujval = UJARG(); 10791573Srgrimes else 10801573Srgrimes ulval = UARG(); 10811573Srgrimes base = 16; 10821573Srgrimes /* leading 0x/X only if non-zero */ 10831573Srgrimes if (flags & ALT && 108487113Sfenner (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) 1085113146Sdas ox[1] = ch; 10861573Srgrimes 108787815Sphantom flags &= ~GROUPING; 10881573Srgrimes /* unsigned conversions */ 10891573Srgrimesnosign: sign = '\0'; 109088057Sphantom /*- 10911573Srgrimes * ``... diouXx conversions ... if a precision is 10921573Srgrimes * specified, the 0 flag will be ignored.'' 10931573Srgrimes * -- ANSI X3J11 10941573Srgrimes */ 10951573Srgrimesnumber: if ((dprec = prec) >= 0) 10961573Srgrimes flags &= ~ZEROPAD; 10971573Srgrimes 109888057Sphantom /*- 10991573Srgrimes * ``The result of converting a zero value with an 11001573Srgrimes * explicit precision of zero is no characters.'' 11011573Srgrimes * -- ANSI X3J11 11021573Srgrimes */ 11031573Srgrimes cp = buf + BUF; 110487113Sfenner if (flags & INTMAX_SIZE) { 110587113Sfenner if (ujval != 0 || prec != 0) 110687113Sfenner cp = __ujtoa(ujval, cp, base, 110787815Sphantom flags & ALT, xdigs, 110887815Sphantom flags & GROUPING, thousands_sep, 110987815Sphantom grouping); 11101573Srgrimes } else { 11111573Srgrimes if (ulval != 0 || prec != 0) 11121573Srgrimes cp = __ultoa(ulval, cp, base, 111387815Sphantom flags & ALT, xdigs, 111487815Sphantom flags & GROUPING, thousands_sep, 111587815Sphantom grouping); 11161573Srgrimes } 11171573Srgrimes size = buf + BUF - cp; 1118113142Sdas if (size > BUF) /* should never happen */ 1119113142Sdas abort(); 11201573Srgrimes break; 11211573Srgrimes default: /* "%?" prints ?, unless ? is NUL */ 11221573Srgrimes if (ch == '\0') 11231573Srgrimes goto done; 11241573Srgrimes /* pretend it was %c with argument ch */ 11251573Srgrimes cp = buf; 11261573Srgrimes *cp = ch; 11271573Srgrimes size = 1; 11281573Srgrimes sign = '\0'; 11291573Srgrimes break; 11301573Srgrimes } 11311573Srgrimes 11321573Srgrimes /* 11331573Srgrimes * All reasonable formats wind up here. At this point, `cp' 11341573Srgrimes * points to a string which (if not flags&LADJUST) should be 11351573Srgrimes * padded out to `width' places. If flags&ZEROPAD, it should 11361573Srgrimes * first be prefixed by any sign or other prefix; otherwise, 11371573Srgrimes * it should be blank padded before the prefix is emitted. 11381573Srgrimes * After any left-hand padding and prefixing, emit zeroes 11391573Srgrimes * required by a decimal [diouxX] precision, then print the 11401573Srgrimes * string proper, then emit zeroes required by any leftover 11411573Srgrimes * floating precision; finally, if LADJUST, pad with blanks. 11421573Srgrimes * 11431573Srgrimes * Compute actual size, so we know how much to pad. 114414727Sfenner * size excludes decimal prec; realsz includes it. 11451573Srgrimes */ 114614727Sfenner realsz = dprec > size ? dprec : size; 11471573Srgrimes if (sign) 114814727Sfenner realsz++; 1149124657Sdas if (ox[1]) 115014727Sfenner realsz += 2; 11511573Srgrimes 115231983Sache prsize = width > realsz ? width : realsz; 115332253Sache if ((unsigned)ret + prsize > INT_MAX) { 115431983Sache ret = EOF; 115531983Sache goto error; 115631983Sache } 115731983Sache 11581573Srgrimes /* right-adjusting blank padding */ 11591573Srgrimes if ((flags & (LADJUST|ZEROPAD)) == 0) 11601573Srgrimes PAD(width - realsz, blanks); 11611573Srgrimes 11621573Srgrimes /* prefix */ 1163124657Sdas if (sign) 11641573Srgrimes PRINT(&sign, 1); 1165124657Sdas 1166124657Sdas if (ox[1]) { /* ox[1] is either x, X, or \0 */ 11671573Srgrimes ox[0] = '0'; 11681573Srgrimes PRINT(ox, 2); 11691573Srgrimes } 11701573Srgrimes 11711573Srgrimes /* right-adjusting zero padding */ 11721573Srgrimes if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 11731573Srgrimes PAD(width - realsz, zeroes); 11741573Srgrimes 11751573Srgrimes /* leading zeroes from decimal precision */ 117614727Sfenner PAD(dprec - size, zeroes); 11771573Srgrimes 11781573Srgrimes /* the string or number proper */ 11791573Srgrimes#ifdef FLOATING_POINT 11801573Srgrimes if ((flags & FPT) == 0) { 11811573Srgrimes PRINT(cp, size); 11821573Srgrimes } else { /* glue together f_p fragments */ 1183113146Sdas if (!expchar) { /* %[fF] or sufficiently short %[gG] */ 1184113146Sdas if (expt <= 0) { 1185113468Sdas PRINT(zeroes, 1); 1186113468Sdas if (prec || flags & ALT) 1187113468Sdas PRINT(decimal_point, 1); 11881573Srgrimes PAD(-expt, zeroes); 1189113191Sdas /* already handled initial 0's */ 1190113191Sdas prec += expt; 11911573Srgrimes } else { 1192113191Sdas PRINTANDPAD(cp, dtoaend, lead, zeroes); 1193113146Sdas cp += lead; 1194113146Sdas if (grouping) { 1195113146Sdas while (nseps>0 || nrepeats>0) { 1196113146Sdas if (nrepeats > 0) 1197113146Sdas nrepeats--; 1198113146Sdas else { 1199113146Sdas grouping--; 1200113146Sdas nseps--; 1201113146Sdas } 1202113146Sdas PRINT(&thousands_sep, 1203113146Sdas 1); 1204113191Sdas PRINTANDPAD(cp,dtoaend, 1205113191Sdas *grouping, zeroes); 1206113146Sdas cp += *grouping; 1207113146Sdas } 1208113191Sdas if (cp > dtoaend) 1209113191Sdas cp = dtoaend; 1210113146Sdas } 1211113146Sdas if (prec || flags & ALT) 1212113146Sdas PRINT(decimal_point,1); 12131573Srgrimes } 1214113191Sdas PRINTANDPAD(cp, dtoaend, prec, zeroes); 1215113146Sdas } else { /* %[eE] or sufficiently long %[gG] */ 1216113191Sdas if (prec > 1 || flags & ALT) { 1217113146Sdas buf[0] = *cp++; 1218113146Sdas buf[1] = *decimal_point; 1219113146Sdas PRINT(buf, 2); 1220113146Sdas PRINT(cp, ndig-1); 1221113146Sdas PAD(prec - ndig, zeroes); 12221573Srgrimes } else /* XeYYY */ 12231573Srgrimes PRINT(cp, 1); 12241573Srgrimes PRINT(expstr, expsize); 12251573Srgrimes } 12261573Srgrimes } 12271573Srgrimes#else 12281573Srgrimes PRINT(cp, size); 12291573Srgrimes#endif 12301573Srgrimes /* left-adjusting padding (always blank) */ 12311573Srgrimes if (flags & LADJUST) 12321573Srgrimes PAD(width - realsz, blanks); 12331573Srgrimes 12341573Srgrimes /* finally, adjust ret */ 123531983Sache ret += prsize; 12361573Srgrimes 12371573Srgrimes FLUSH(); /* copy out the I/O vectors */ 12381573Srgrimes } 12391573Srgrimesdone: 12401573Srgrimes FLUSH(); 12411573Srgrimeserror: 124272523Stegge#ifdef FLOATING_POINT 124372523Stegge if (dtoaresult != NULL) 1244113146Sdas freedtoa(dtoaresult); 124572523Stegge#endif 1246103633Stjr if (convbuf != NULL) 1247103633Stjr free(convbuf); 124813545Sjulian if (__sferror(fp)) 124913545Sjulian ret = EOF; 1250103399Stjr if ((argtable != NULL) && (argtable != statargtable)) 1251103399Stjr free (argtable); 125213545Sjulian return (ret); 12531573Srgrimes /* NOTREACHED */ 12541573Srgrimes} 12551573Srgrimes 125621674Sjkh/* 125721674Sjkh * Find all arguments when a positional parameter is encountered. Returns a 125821674Sjkh * table, indexed by argument number, of pointers to each arguments. The 125921674Sjkh * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. 126070725Sarchie * It will be replaces with a malloc-ed one if it overflows. 126121674Sjkh */ 126221674Sjkhstatic void 126384962Sbde__find_arguments (const char *fmt0, va_list ap, union arg **argtable) 126421674Sjkh{ 126571579Sdeischen char *fmt; /* format string */ 126671579Sdeischen int ch; /* character from fmt */ 126771579Sdeischen int n, n2; /* handy integer (short term usage) */ 126871579Sdeischen char *cp; /* handy char pointer (short term usage) */ 126971579Sdeischen int flags; /* flags as above */ 127021674Sjkh int width; /* width from format (%8d), or 0 */ 127187113Sfenner enum typeid *typetable; /* table of types */ 127287113Sfenner enum typeid stattypetable [STATIC_ARG_TBL_SIZE]; 127321674Sjkh int tablesize; /* current size of type table */ 127421674Sjkh int tablemax; /* largest used index in table */ 127521674Sjkh int nextarg; /* 1-based argument index */ 127621674Sjkh 127721674Sjkh /* 127821674Sjkh * Add an argument type to the table, expanding if necessary. 127921674Sjkh */ 128021674Sjkh#define ADDTYPE(type) \ 128121674Sjkh ((nextarg >= tablesize) ? \ 128221674Sjkh __grow_type_table(nextarg, &typetable, &tablesize) : 0, \ 128370725Sarchie (nextarg > tablemax) ? tablemax = nextarg : 0, \ 128470725Sarchie typetable[nextarg++] = type) 128521674Sjkh 128621674Sjkh#define ADDSARG() \ 128787113Sfenner ((flags&INTMAXT) ? ADDTYPE(T_INTMAXT) : \ 128887113Sfenner ((flags&SIZET) ? ADDTYPE(T_SIZET) : \ 128987113Sfenner ((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \ 129087113Sfenner ((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \ 129187113Sfenner ((flags&LONGINT) ? ADDTYPE(T_LONG) : ADDTYPE(T_INT)))))) 129221674Sjkh 129321674Sjkh#define ADDUARG() \ 129487113Sfenner ((flags&INTMAXT) ? ADDTYPE(T_UINTMAXT) : \ 129587113Sfenner ((flags&SIZET) ? ADDTYPE(T_SIZET) : \ 129687113Sfenner ((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \ 129787113Sfenner ((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \ 129887113Sfenner ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : ADDTYPE(T_U_INT)))))) 129921674Sjkh 130021674Sjkh /* 130121674Sjkh * Add * arguments to the type array. 130221674Sjkh */ 130321674Sjkh#define ADDASTER() \ 130421674Sjkh n2 = 0; \ 130521674Sjkh cp = fmt; \ 130621674Sjkh while (is_digit(*cp)) { \ 130721674Sjkh n2 = 10 * n2 + to_digit(*cp); \ 130821674Sjkh cp++; \ 130921674Sjkh } \ 131021674Sjkh if (*cp == '$') { \ 131121674Sjkh int hold = nextarg; \ 131221674Sjkh nextarg = n2; \ 131321674Sjkh ADDTYPE (T_INT); \ 131421674Sjkh nextarg = hold; \ 131521674Sjkh fmt = ++cp; \ 131621674Sjkh } else { \ 131721674Sjkh ADDTYPE (T_INT); \ 131821674Sjkh } 131921674Sjkh fmt = (char *)fmt0; 132021674Sjkh typetable = stattypetable; 132121674Sjkh tablesize = STATIC_ARG_TBL_SIZE; 132221674Sjkh tablemax = 0; 132321674Sjkh nextarg = 1; 1324128550Stjr for (n = 0; n < STATIC_ARG_TBL_SIZE; n++) 1325128550Stjr typetable[n] = T_UNUSED; 132621674Sjkh 132721674Sjkh /* 132821674Sjkh * Scan the format for conversions (`%' character). 132921674Sjkh */ 133021674Sjkh for (;;) { 133121674Sjkh for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 133221674Sjkh /* void */; 133321674Sjkh if (ch == '\0') 133421674Sjkh goto done; 133521674Sjkh fmt++; /* skip over '%' */ 133621674Sjkh 133721674Sjkh flags = 0; 133821674Sjkh width = 0; 133921674Sjkh 134021674Sjkhrflag: ch = *fmt++; 134121674Sjkhreswitch: switch (ch) { 134221674Sjkh case ' ': 134321674Sjkh case '#': 134421674Sjkh goto rflag; 134521674Sjkh case '*': 134621674Sjkh ADDASTER (); 134721674Sjkh goto rflag; 134821674Sjkh case '-': 134921674Sjkh case '+': 135087113Sfenner case '\'': 135121674Sjkh goto rflag; 135221674Sjkh case '.': 135321674Sjkh if ((ch = *fmt++) == '*') { 135421674Sjkh ADDASTER (); 135521674Sjkh goto rflag; 135621674Sjkh } 135721674Sjkh while (is_digit(ch)) { 135821674Sjkh ch = *fmt++; 135921674Sjkh } 136021674Sjkh goto reswitch; 136121674Sjkh case '0': 136221674Sjkh goto rflag; 136321674Sjkh case '1': case '2': case '3': case '4': 136421674Sjkh case '5': case '6': case '7': case '8': case '9': 136521674Sjkh n = 0; 136621674Sjkh do { 136721674Sjkh n = 10 * n + to_digit(ch); 136821674Sjkh ch = *fmt++; 136921674Sjkh } while (is_digit(ch)); 137021674Sjkh if (ch == '$') { 137121674Sjkh nextarg = n; 137221674Sjkh goto rflag; 137321674Sjkh } 137421674Sjkh width = n; 137521674Sjkh goto reswitch; 13761573Srgrimes#ifdef FLOATING_POINT 137721674Sjkh case 'L': 137821674Sjkh flags |= LONGDBL; 137921674Sjkh goto rflag; 138021674Sjkh#endif 138121674Sjkh case 'h': 138287113Sfenner if (flags & SHORTINT) { 138387113Sfenner flags &= ~SHORTINT; 138487113Sfenner flags |= CHARINT; 138587113Sfenner } else 138687113Sfenner flags |= SHORTINT; 138721674Sjkh goto rflag; 138887113Sfenner case 'j': 138987113Sfenner flags |= INTMAXT; 139087113Sfenner goto rflag; 139121674Sjkh case 'l': 139287113Sfenner if (flags & LONGINT) { 139387113Sfenner flags &= ~LONGINT; 139487113Sfenner flags |= LLONGINT; 139587113Sfenner } else 139644674Sdfr flags |= LONGINT; 139721674Sjkh goto rflag; 139821674Sjkh case 'q': 139987113Sfenner flags |= LLONGINT; /* not necessarily */ 140021674Sjkh goto rflag; 140187113Sfenner case 't': 140287113Sfenner flags |= PTRDIFFT; 140387113Sfenner goto rflag; 140487113Sfenner case 'z': 140587113Sfenner flags |= SIZET; 140687113Sfenner goto rflag; 1407105204Stjr case 'C': 1408105204Stjr flags |= LONGINT; 1409105204Stjr /*FALLTHROUGH*/ 141021674Sjkh case 'c': 1411103633Stjr if (flags & LONGINT) 1412103633Stjr ADDTYPE(T_WINT); 1413103633Stjr else 1414103633Stjr ADDTYPE(T_INT); 141521674Sjkh break; 141621674Sjkh case 'D': 141721674Sjkh flags |= LONGINT; 141821674Sjkh /*FALLTHROUGH*/ 141921674Sjkh case 'd': 142021674Sjkh case 'i': 142187113Sfenner ADDSARG(); 142221674Sjkh break; 142321674Sjkh#ifdef FLOATING_POINT 142487113Sfenner#ifdef HEXFLOAT 142587113Sfenner case 'a': 142687113Sfenner case 'A': 142787113Sfenner#endif 142821674Sjkh case 'e': 142921674Sjkh case 'E': 143021674Sjkh case 'f': 143121674Sjkh case 'g': 143221674Sjkh case 'G': 143321674Sjkh if (flags & LONGDBL) 143421674Sjkh ADDTYPE(T_LONG_DOUBLE); 143521674Sjkh else 143621674Sjkh ADDTYPE(T_DOUBLE); 143721674Sjkh break; 143821674Sjkh#endif /* FLOATING_POINT */ 143921674Sjkh case 'n': 144087113Sfenner if (flags & INTMAXT) 144187113Sfenner ADDTYPE(TP_INTMAXT); 144287113Sfenner else if (flags & PTRDIFFT) 144387113Sfenner ADDTYPE(TP_PTRDIFFT); 144487113Sfenner else if (flags & SIZET) 144587113Sfenner ADDTYPE(TP_SIZET); 144687113Sfenner else if (flags & LLONGINT) 144787113Sfenner ADDTYPE(TP_LLONG); 144821674Sjkh else if (flags & LONGINT) 144921674Sjkh ADDTYPE(TP_LONG); 145021674Sjkh else if (flags & SHORTINT) 145121674Sjkh ADDTYPE(TP_SHORT); 145287113Sfenner else if (flags & CHARINT) 145387113Sfenner ADDTYPE(TP_SCHAR); 145421674Sjkh else 145521674Sjkh ADDTYPE(TP_INT); 145621674Sjkh continue; /* no output */ 145721674Sjkh case 'O': 145821674Sjkh flags |= LONGINT; 145921674Sjkh /*FALLTHROUGH*/ 146021674Sjkh case 'o': 146187113Sfenner ADDUARG(); 146221674Sjkh break; 146321674Sjkh case 'p': 146421674Sjkh ADDTYPE(TP_VOID); 146521674Sjkh break; 1466105204Stjr case 'S': 1467105204Stjr flags |= LONGINT; 1468105204Stjr /*FALLTHROUGH*/ 146921674Sjkh case 's': 1470103633Stjr if (flags & LONGINT) 1471103633Stjr ADDTYPE(TP_WCHAR); 1472103633Stjr else 1473103633Stjr ADDTYPE(TP_CHAR); 147421674Sjkh break; 147521674Sjkh case 'U': 147621674Sjkh flags |= LONGINT; 147721674Sjkh /*FALLTHROUGH*/ 147821674Sjkh case 'u': 147921674Sjkh case 'X': 148021674Sjkh case 'x': 148187113Sfenner ADDUARG(); 148221674Sjkh break; 148321674Sjkh default: /* "%?" prints ?, unless ? is NUL */ 148421674Sjkh if (ch == '\0') 148521674Sjkh goto done; 148621674Sjkh break; 148721674Sjkh } 148821674Sjkh } 148921674Sjkhdone: 149021674Sjkh /* 149121674Sjkh * Build the argument table. 149221674Sjkh */ 149321674Sjkh if (tablemax >= STATIC_ARG_TBL_SIZE) { 149484922Sdfr *argtable = (union arg *) 149584922Sdfr malloc (sizeof (union arg) * (tablemax + 1)); 149621674Sjkh } 14971573Srgrimes 149884922Sdfr (*argtable) [0].intarg = 0; 149921674Sjkh for (n = 1; n <= tablemax; n++) { 150021674Sjkh switch (typetable [n]) { 150187113Sfenner case T_UNUSED: /* whoops! */ 150284922Sdfr (*argtable) [n].intarg = va_arg (ap, int); 150321674Sjkh break; 150487113Sfenner case TP_SCHAR: 150587113Sfenner (*argtable) [n].pschararg = va_arg (ap, signed char *); 150621674Sjkh break; 150721674Sjkh case TP_SHORT: 150884922Sdfr (*argtable) [n].pshortarg = va_arg (ap, short *); 150921674Sjkh break; 151021674Sjkh case T_INT: 151184922Sdfr (*argtable) [n].intarg = va_arg (ap, int); 151221674Sjkh break; 151321674Sjkh case T_U_INT: 151484922Sdfr (*argtable) [n].uintarg = va_arg (ap, unsigned int); 151521674Sjkh break; 151621674Sjkh case TP_INT: 151784922Sdfr (*argtable) [n].pintarg = va_arg (ap, int *); 151821674Sjkh break; 151921674Sjkh case T_LONG: 152084922Sdfr (*argtable) [n].longarg = va_arg (ap, long); 152121674Sjkh break; 152221674Sjkh case T_U_LONG: 152384922Sdfr (*argtable) [n].ulongarg = va_arg (ap, unsigned long); 152421674Sjkh break; 152521674Sjkh case TP_LONG: 152684922Sdfr (*argtable) [n].plongarg = va_arg (ap, long *); 152721674Sjkh break; 152887113Sfenner case T_LLONG: 152987113Sfenner (*argtable) [n].longlongarg = va_arg (ap, long long); 153021674Sjkh break; 153187113Sfenner case T_U_LLONG: 153287113Sfenner (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long); 153321674Sjkh break; 153487113Sfenner case TP_LLONG: 153587113Sfenner (*argtable) [n].plonglongarg = va_arg (ap, long long *); 153621674Sjkh break; 153787113Sfenner case T_PTRDIFFT: 153887113Sfenner (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); 153987113Sfenner break; 154087113Sfenner case TP_PTRDIFFT: 154187113Sfenner (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); 154287113Sfenner break; 154387113Sfenner case T_SIZET: 154487113Sfenner (*argtable) [n].sizearg = va_arg (ap, size_t); 154587113Sfenner break; 154687113Sfenner case TP_SIZET: 154787113Sfenner (*argtable) [n].psizearg = va_arg (ap, ssize_t *); 154887113Sfenner break; 154987113Sfenner case T_INTMAXT: 155087113Sfenner (*argtable) [n].intmaxarg = va_arg (ap, intmax_t); 155187113Sfenner break; 155287113Sfenner case T_UINTMAXT: 155387113Sfenner (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); 155487113Sfenner break; 155587113Sfenner case TP_INTMAXT: 155687113Sfenner (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); 155787113Sfenner break; 155884922Sdfr#ifdef FLOATING_POINT 155921674Sjkh case T_DOUBLE: 156084922Sdfr (*argtable) [n].doublearg = va_arg (ap, double); 156121674Sjkh break; 156221674Sjkh case T_LONG_DOUBLE: 156384922Sdfr (*argtable) [n].longdoublearg = va_arg (ap, long double); 156421674Sjkh break; 156584922Sdfr#endif 156621674Sjkh case TP_CHAR: 156784922Sdfr (*argtable) [n].pchararg = va_arg (ap, char *); 156821674Sjkh break; 156921674Sjkh case TP_VOID: 157084922Sdfr (*argtable) [n].pvoidarg = va_arg (ap, void *); 157121674Sjkh break; 1572103633Stjr case T_WINT: 1573103633Stjr (*argtable) [n].wintarg = va_arg (ap, wint_t); 1574103633Stjr break; 1575103633Stjr case TP_WCHAR: 1576103633Stjr (*argtable) [n].pwchararg = va_arg (ap, wchar_t *); 1577103633Stjr break; 157821674Sjkh } 157921674Sjkh } 158021674Sjkh 158121674Sjkh if ((typetable != NULL) && (typetable != stattypetable)) 158221674Sjkh free (typetable); 158321674Sjkh} 158421674Sjkh 158521674Sjkh/* 158621674Sjkh * Increase the size of the type table. 158721674Sjkh */ 158821674Sjkhstatic void 158987113Sfenner__grow_type_table (int nextarg, enum typeid **typetable, int *tablesize) 159021674Sjkh{ 159187113Sfenner enum typeid *const oldtable = *typetable; 159270725Sarchie const int oldsize = *tablesize; 159387113Sfenner enum typeid *newtable; 1594128550Stjr int n, newsize = oldsize * 2; 159521674Sjkh 159670725Sarchie if (newsize < nextarg + 1) 159770725Sarchie newsize = nextarg + 1; 159870725Sarchie if (oldsize == STATIC_ARG_TBL_SIZE) { 1599128550Stjr if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL) 160070725Sarchie abort(); /* XXX handle better */ 1601128550Stjr bcopy(oldtable, newtable, oldsize * sizeof(enum typeid)); 160221674Sjkh } else { 1603128550Stjr newtable = reallocf(oldtable, newsize * sizeof(enum typeid)); 1604128550Stjr if (newtable == NULL) 160570725Sarchie abort(); /* XXX handle better */ 160621674Sjkh } 1607128550Stjr for (n = oldsize; n < newsize; n++) 1608128550Stjr newtable[n] = T_UNUSED; 160921674Sjkh 161070725Sarchie *typetable = newtable; 161121674Sjkh *tablesize = newsize; 161221674Sjkh} 161321674Sjkh 161421674Sjkh 161521674Sjkh#ifdef FLOATING_POINT 161621674Sjkh 16171573Srgrimesstatic int 161871579Sdeischenexponent(char *p0, int exp, int fmtch) 16191573Srgrimes{ 162071579Sdeischen char *p, *t; 1621113142Sdas char expbuf[MAXEXPDIG]; 16221573Srgrimes 16231573Srgrimes p = p0; 16241573Srgrimes *p++ = fmtch; 16251573Srgrimes if (exp < 0) { 16261573Srgrimes exp = -exp; 16271573Srgrimes *p++ = '-'; 16281573Srgrimes } 16291573Srgrimes else 16301573Srgrimes *p++ = '+'; 1631113142Sdas t = expbuf + MAXEXPDIG; 16321573Srgrimes if (exp > 9) { 16331573Srgrimes do { 16341573Srgrimes *--t = to_char(exp % 10); 16351573Srgrimes } while ((exp /= 10) > 9); 16361573Srgrimes *--t = to_char(exp); 1637113142Sdas for (; t < expbuf + MAXEXPDIG; *p++ = *t++); 16381573Srgrimes } 16391573Srgrimes else { 1640113146Sdas /* 1641113146Sdas * Exponents for decimal floating point conversions 1642113146Sdas * (%[eEgG]) must be at least two characters long, 1643113146Sdas * whereas exponents for hexadecimal conversions can 1644113146Sdas * be only one character long. 1645113146Sdas */ 1646113146Sdas if (fmtch == 'e' || fmtch == 'E') 1647113146Sdas *p++ = '0'; 16481573Srgrimes *p++ = to_char(exp); 16491573Srgrimes } 16501573Srgrimes return (p - p0); 16511573Srgrimes} 16521573Srgrimes#endif /* FLOATING_POINT */ 1653