158f0484fSRodney W. Grimes /*- 258f0484fSRodney W. Grimes * Copyright (c) 1990, 1993 358f0484fSRodney W. Grimes * The Regents of the University of California. All rights reserved. 458f0484fSRodney W. Grimes * 558f0484fSRodney W. Grimes * This code is derived from software contributed to Berkeley by 658f0484fSRodney W. Grimes * Chris Torek. 758f0484fSRodney W. Grimes * 858f0484fSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 958f0484fSRodney W. Grimes * modification, are permitted provided that the following conditions 1058f0484fSRodney W. Grimes * are met: 1158f0484fSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 1258f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 1358f0484fSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 1458f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 1558f0484fSRodney W. Grimes * documentation and/or other materials provided with the distribution. 1658f0484fSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 1758f0484fSRodney W. Grimes * may be used to endorse or promote products derived from this software 1858f0484fSRodney W. Grimes * without specific prior written permission. 1958f0484fSRodney W. Grimes * 2058f0484fSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2158f0484fSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2258f0484fSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2358f0484fSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2458f0484fSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2558f0484fSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2658f0484fSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2758f0484fSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2858f0484fSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2958f0484fSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3058f0484fSRodney W. Grimes * SUCH DAMAGE. 3158f0484fSRodney W. Grimes */ 3258f0484fSRodney W. Grimes 3358f0484fSRodney W. Grimes #if defined(LIBC_SCCS) && !defined(lint) 3458f0484fSRodney W. Grimes static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; 3558f0484fSRodney W. Grimes #endif /* LIBC_SCCS and not lint */ 36333fc21eSDavid E. O'Brien #include <sys/cdefs.h> 37333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 3858f0484fSRodney W. Grimes 3958f0484fSRodney W. Grimes /* 4058f0484fSRodney W. Grimes * Actual printf innards. 4158f0484fSRodney W. Grimes * 4258f0484fSRodney W. Grimes * This code is large and complicated... 4358f0484fSRodney W. Grimes */ 4458f0484fSRodney W. Grimes 45d201fe46SDaniel Eischen #include "namespace.h" 4658f0484fSRodney W. Grimes #include <sys/types.h> 4758f0484fSRodney W. Grimes 487735bb0fSBill Fenner #include <ctype.h> 4958f0484fSRodney W. Grimes #include <limits.h> 507ae5c679SAlexey Zelkin #include <locale.h> 517735bb0fSBill Fenner #include <stddef.h> 527735bb0fSBill Fenner #include <stdint.h> 5358f0484fSRodney W. Grimes #include <stdio.h> 5458f0484fSRodney W. Grimes #include <stdlib.h> 5558f0484fSRodney W. Grimes #include <string.h> 56b9aac308STim J. Robbins #include <wchar.h> 5775067f4fSPoul-Henning Kamp #include <printf.h> 5858f0484fSRodney W. Grimes 5958f0484fSRodney W. Grimes #include <stdarg.h> 60d201fe46SDaniel Eischen #include "un-namespace.h" 6158f0484fSRodney W. Grimes 62d201fe46SDaniel Eischen #include "libc_private.h" 6358f0484fSRodney W. Grimes #include "local.h" 6458f0484fSRodney W. Grimes #include "fvwrite.h" 652591efccSDavid Schultz #include "printflocal.h" 66e5abb5e6SDavid Schultz 67c05ac53bSDavid E. O'Brien static int __sprint(FILE *, struct __suio *); 68a1805f7bSDavid Schultz static int __sbprintf(FILE *, const char *, va_list) __printflike(2, 0) 69a1805f7bSDavid Schultz __noinline; 70b9aac308STim J. Robbins static char *__wcsconv(wchar_t *, int); 71ce51cf03SJames Raynard 72814d1bc9SDavid Schultz #define CHAR char 73814d1bc9SDavid Schultz #include "printfcommon.h" 74814d1bc9SDavid Schultz 7521ca178eSDavid Schultz struct grouping_state { 7621ca178eSDavid Schultz char *thousands_sep; /* locale-specific thousands separator */ 7721ca178eSDavid Schultz int thousep_len; /* length of thousands_sep */ 7821ca178eSDavid Schultz const char *grouping; /* locale-specific numeric grouping rules */ 7921ca178eSDavid Schultz int lead; /* sig figs before decimal or group sep */ 8021ca178eSDavid Schultz int nseps; /* number of group separators with ' */ 8121ca178eSDavid Schultz int nrepeats; /* number of repeats of the last group */ 8221ca178eSDavid Schultz }; 8321ca178eSDavid Schultz 8421ca178eSDavid Schultz /* 8521ca178eSDavid Schultz * Initialize the thousands' grouping state in preparation to print a 8621ca178eSDavid Schultz * number with ndigits digits. This routine returns the total number 8721ca178eSDavid Schultz * of bytes that will be needed. 8821ca178eSDavid Schultz */ 8921ca178eSDavid Schultz static int 9021ca178eSDavid Schultz grouping_init(struct grouping_state *gs, int ndigits) 9121ca178eSDavid Schultz { 9221ca178eSDavid Schultz struct lconv *locale; 9321ca178eSDavid Schultz 9421ca178eSDavid Schultz locale = localeconv(); 9521ca178eSDavid Schultz gs->grouping = locale->grouping; 9621ca178eSDavid Schultz gs->thousands_sep = locale->thousands_sep; 9721ca178eSDavid Schultz gs->thousep_len = strlen(gs->thousands_sep); 9821ca178eSDavid Schultz 9921ca178eSDavid Schultz gs->nseps = gs->nrepeats = 0; 10021ca178eSDavid Schultz gs->lead = ndigits; 10121ca178eSDavid Schultz while (*gs->grouping != CHAR_MAX) { 10221ca178eSDavid Schultz if (gs->lead <= *gs->grouping) 10321ca178eSDavid Schultz break; 10421ca178eSDavid Schultz gs->lead -= *gs->grouping; 10521ca178eSDavid Schultz if (*(gs->grouping+1)) { 10621ca178eSDavid Schultz gs->nseps++; 10721ca178eSDavid Schultz gs->grouping++; 10821ca178eSDavid Schultz } else 10921ca178eSDavid Schultz gs->nrepeats++; 11021ca178eSDavid Schultz } 11121ca178eSDavid Schultz return ((gs->nseps + gs->nrepeats) * gs->thousep_len); 11221ca178eSDavid Schultz } 11321ca178eSDavid Schultz 11421ca178eSDavid Schultz /* 11521ca178eSDavid Schultz * Print a number with thousands' separators. 11621ca178eSDavid Schultz */ 11721ca178eSDavid Schultz static int 11821ca178eSDavid Schultz grouping_print(struct grouping_state *gs, struct io_state *iop, 11921ca178eSDavid Schultz const CHAR *cp, const CHAR *ep) 12021ca178eSDavid Schultz { 12121ca178eSDavid Schultz const CHAR *cp0 = cp; 12221ca178eSDavid Schultz 12321ca178eSDavid Schultz if (io_printandpad(iop, cp, ep, gs->lead, zeroes)) 12421ca178eSDavid Schultz return (-1); 12521ca178eSDavid Schultz cp += gs->lead; 12621ca178eSDavid Schultz while (gs->nseps > 0 || gs->nrepeats > 0) { 12721ca178eSDavid Schultz if (gs->nrepeats > 0) 12821ca178eSDavid Schultz gs->nrepeats--; 12921ca178eSDavid Schultz else { 13021ca178eSDavid Schultz gs->grouping--; 13121ca178eSDavid Schultz gs->nseps--; 13221ca178eSDavid Schultz } 13321ca178eSDavid Schultz if (io_print(iop, gs->thousands_sep, gs->thousep_len)) 13421ca178eSDavid Schultz return (-1); 13521ca178eSDavid Schultz if (io_printandpad(iop, cp, ep, *gs->grouping, zeroes)) 13621ca178eSDavid Schultz return (-1); 13721ca178eSDavid Schultz cp += *gs->grouping; 13821ca178eSDavid Schultz } 13921ca178eSDavid Schultz if (cp > ep) 14021ca178eSDavid Schultz cp = ep; 14121ca178eSDavid Schultz return (cp - cp0); 14221ca178eSDavid Schultz } 14321ca178eSDavid Schultz 14458f0484fSRodney W. Grimes /* 14558f0484fSRodney W. Grimes * Flush out all the vectors defined by the given uio, 14658f0484fSRodney W. Grimes * then reset it so that it can be reused. 14758f0484fSRodney W. Grimes */ 14858f0484fSRodney W. Grimes static int 149d201fe46SDaniel Eischen __sprint(FILE *fp, struct __suio *uio) 15058f0484fSRodney W. Grimes { 151d201fe46SDaniel Eischen int err; 15258f0484fSRodney W. Grimes 15358f0484fSRodney W. Grimes if (uio->uio_resid == 0) { 15458f0484fSRodney W. Grimes uio->uio_iovcnt = 0; 15558f0484fSRodney W. Grimes return (0); 15658f0484fSRodney W. Grimes } 15758f0484fSRodney W. Grimes err = __sfvwrite(fp, uio); 15858f0484fSRodney W. Grimes uio->uio_resid = 0; 15958f0484fSRodney W. Grimes uio->uio_iovcnt = 0; 16058f0484fSRodney W. Grimes return (err); 16158f0484fSRodney W. Grimes } 16258f0484fSRodney W. Grimes 16358f0484fSRodney W. Grimes /* 16458f0484fSRodney W. Grimes * Helper function for `fprintf to unbuffered unix file': creates a 16558f0484fSRodney W. Grimes * temporary buffer. We only work on write-only files; this avoids 16658f0484fSRodney W. Grimes * worries about ungetc buffers and so forth. 16758f0484fSRodney W. Grimes */ 16858f0484fSRodney W. Grimes static int 169d201fe46SDaniel Eischen __sbprintf(FILE *fp, const char *fmt, va_list ap) 17058f0484fSRodney W. Grimes { 17158f0484fSRodney W. Grimes int ret; 17258f0484fSRodney W. Grimes FILE fake; 17358f0484fSRodney W. Grimes unsigned char buf[BUFSIZ]; 17458f0484fSRodney W. Grimes 175a1805f7bSDavid Schultz /* XXX This is probably not needed. */ 176a1805f7bSDavid Schultz if (prepwrite(fp) != 0) 177a1805f7bSDavid Schultz return (EOF); 178a1805f7bSDavid Schultz 17958f0484fSRodney W. Grimes /* copy the important variables */ 18058f0484fSRodney W. Grimes fake._flags = fp->_flags & ~__SNBF; 18158f0484fSRodney W. Grimes fake._file = fp->_file; 18258f0484fSRodney W. Grimes fake._cookie = fp->_cookie; 18358f0484fSRodney W. Grimes fake._write = fp->_write; 1841e98f887SJohn Baldwin fake._orientation = fp->_orientation; 1851e98f887SJohn Baldwin fake._mbstate = fp->_mbstate; 18658f0484fSRodney W. Grimes 18758f0484fSRodney W. Grimes /* set up the buffer */ 18858f0484fSRodney W. Grimes fake._bf._base = fake._p = buf; 18958f0484fSRodney W. Grimes fake._bf._size = fake._w = sizeof(buf); 19058f0484fSRodney W. Grimes fake._lbfsize = 0; /* not actually used, but Just In Case */ 19158f0484fSRodney W. Grimes 19258f0484fSRodney W. Grimes /* do the work, then copy any error status */ 193d201fe46SDaniel Eischen ret = __vfprintf(&fake, fmt, ap); 194d201fe46SDaniel Eischen if (ret >= 0 && __fflush(&fake)) 19558f0484fSRodney W. Grimes ret = EOF; 19658f0484fSRodney W. Grimes if (fake._flags & __SERR) 19758f0484fSRodney W. Grimes fp->_flags |= __SERR; 19858f0484fSRodney W. Grimes return (ret); 19958f0484fSRodney W. Grimes } 20058f0484fSRodney W. Grimes 20158f0484fSRodney W. Grimes /* 202b9aac308STim J. Robbins * Convert a wide character string argument for the %ls format to a multibyte 203d48c77b5STim J. Robbins * string representation. If not -1, prec specifies the maximum number of 204d48c77b5STim J. Robbins * bytes to output, and also means that we can't assume that the wide char. 205d48c77b5STim J. Robbins * string ends is null-terminated. 206b9aac308STim J. Robbins */ 207b9aac308STim J. Robbins static char * 208b9aac308STim J. Robbins __wcsconv(wchar_t *wcsarg, int prec) 209b9aac308STim J. Robbins { 21093996f6dSTim J. Robbins static const mbstate_t initial; 21193996f6dSTim J. Robbins mbstate_t mbs; 212b9aac308STim J. Robbins char buf[MB_LEN_MAX]; 213b9aac308STim J. Robbins wchar_t *p; 214d48c77b5STim J. Robbins char *convbuf; 215b9aac308STim J. Robbins size_t clen, nbytes; 216b9aac308STim J. Robbins 217d48c77b5STim J. Robbins /* Allocate space for the maximum number of bytes we could output. */ 218d48c77b5STim J. Robbins if (prec < 0) { 219d48c77b5STim J. Robbins p = wcsarg; 220d48c77b5STim J. Robbins mbs = initial; 221d48c77b5STim J. Robbins nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); 222d48c77b5STim J. Robbins if (nbytes == (size_t)-1) 223d48c77b5STim J. Robbins return (NULL); 224d48c77b5STim J. Robbins } else { 225b9aac308STim J. Robbins /* 226d48c77b5STim J. Robbins * Optimisation: if the output precision is small enough, 227d48c77b5STim J. Robbins * just allocate enough memory for the maximum instead of 228d48c77b5STim J. Robbins * scanning the string. 229b9aac308STim J. Robbins */ 230d48c77b5STim J. Robbins if (prec < 128) 231d48c77b5STim J. Robbins nbytes = prec; 232d48c77b5STim J. Robbins else { 233b9aac308STim J. Robbins nbytes = 0; 234b9aac308STim J. Robbins p = wcsarg; 23593996f6dSTim J. Robbins mbs = initial; 236b9aac308STim J. Robbins for (;;) { 23793996f6dSTim J. Robbins clen = wcrtomb(buf, *p++, &mbs); 238b9aac308STim J. Robbins if (clen == 0 || clen == (size_t)-1 || 239b9aac308STim J. Robbins nbytes + clen > prec) 240b9aac308STim J. Robbins break; 241b9aac308STim J. Robbins nbytes += clen; 242b9aac308STim J. Robbins } 243d48c77b5STim J. Robbins } 244b9aac308STim J. Robbins } 245b9aac308STim J. Robbins if ((convbuf = malloc(nbytes + 1)) == NULL) 246b9aac308STim J. Robbins return (NULL); 247b9aac308STim J. Robbins 248d48c77b5STim J. Robbins /* Fill the output buffer. */ 249b9aac308STim J. Robbins p = wcsarg; 25093996f6dSTim J. Robbins mbs = initial; 251d48c77b5STim J. Robbins if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, 252d48c77b5STim J. Robbins nbytes, &mbs)) == (size_t)-1) { 2536f098a48SAndrey A. Chernov free(convbuf); 254b9aac308STim J. Robbins return (NULL); 2556f098a48SAndrey A. Chernov } 256d48c77b5STim J. Robbins convbuf[nbytes] = '\0'; 257b9aac308STim J. Robbins return (convbuf); 258b9aac308STim J. Robbins } 259b9aac308STim J. Robbins 260b9aac308STim J. Robbins /* 261d201fe46SDaniel Eischen * MT-safe version 262d201fe46SDaniel Eischen */ 263d201fe46SDaniel Eischen int 264f8418db7SRobert Drehmel vfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap) 265f8418db7SRobert Drehmel 266d201fe46SDaniel Eischen { 267d201fe46SDaniel Eischen int ret; 268d201fe46SDaniel Eischen 269d201fe46SDaniel Eischen FLOCKFILE(fp); 270a1805f7bSDavid Schultz /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 271a1805f7bSDavid Schultz if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 272a1805f7bSDavid Schultz fp->_file >= 0) 273a1805f7bSDavid Schultz ret = __sbprintf(fp, fmt0, ap); 274a1805f7bSDavid Schultz else 275d201fe46SDaniel Eischen ret = __vfprintf(fp, fmt0, ap); 276d201fe46SDaniel Eischen FUNLOCKFILE(fp); 277d201fe46SDaniel Eischen return (ret); 278d201fe46SDaniel Eischen } 279d201fe46SDaniel Eischen 28038cac8f8SDavid Schultz /* 28138cac8f8SDavid Schultz * The size of the buffer we use as scratch space for integer 28221ca178eSDavid Schultz * conversions, among other things. We need enough space to 28321ca178eSDavid Schultz * write a uintmax_t in octal (plus one byte). 28438cac8f8SDavid Schultz */ 28521ca178eSDavid Schultz #if UINTMAX_MAX <= UINT64_MAX 28621ca178eSDavid Schultz #define BUF 32 28721ca178eSDavid Schultz #else 28821ca178eSDavid Schultz #error "BUF must be large enough to format a uintmax_t" 28921ca178eSDavid Schultz #endif 29038cac8f8SDavid Schultz 29158f0484fSRodney W. Grimes /* 292d201fe46SDaniel Eischen * Non-MT-safe version 293d201fe46SDaniel Eischen */ 29458f0484fSRodney W. Grimes int 295d201fe46SDaniel Eischen __vfprintf(FILE *fp, const char *fmt0, va_list ap) 29658f0484fSRodney W. Grimes { 297d201fe46SDaniel Eischen char *fmt; /* format string */ 298d201fe46SDaniel Eischen int ch; /* character from fmt */ 299d201fe46SDaniel Eischen int n, n2; /* handy integer (short term usage) */ 300d201fe46SDaniel Eischen char *cp; /* handy char pointer (short term usage) */ 301d201fe46SDaniel Eischen int flags; /* flags as above */ 30258f0484fSRodney W. Grimes int ret; /* return value accumulator */ 30358f0484fSRodney W. Grimes int width; /* width from format (%8d), or 0 */ 304ebbad5ecSDavid Schultz int prec; /* precision from format; <0 for N/A */ 30558f0484fSRodney W. Grimes char sign; /* sign prefix (' ', '+', '-', or \0) */ 30621ca178eSDavid Schultz struct grouping_state gs; /* thousands' grouping info */ 307814d1bc9SDavid Schultz 3088de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 309ebbad5ecSDavid Schultz /* 310ebbad5ecSDavid Schultz * We can decompose the printed representation of floating 311ebbad5ecSDavid Schultz * point numbers into several parts, some of which may be empty: 312ebbad5ecSDavid Schultz * 313ebbad5ecSDavid Schultz * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ 314ebbad5ecSDavid Schultz * A B ---C--- D E F 315ebbad5ecSDavid Schultz * 316ebbad5ecSDavid Schultz * A: 'sign' holds this value if present; '\0' otherwise 317ebbad5ecSDavid Schultz * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal 318ebbad5ecSDavid Schultz * C: cp points to the string MMMNNN. Leading and trailing 319ebbad5ecSDavid Schultz * zeros are not in the string and must be added. 320ebbad5ecSDavid Schultz * D: expchar holds this character; '\0' if no exponent, e.g. %f 321ebbad5ecSDavid Schultz * F: at least two digits for decimal, at least one digit for hex 322ebbad5ecSDavid Schultz */ 3237ae5c679SAlexey Zelkin char *decimal_point; /* locale specific decimal point */ 3245004a238SDavid Schultz int decpt_len; /* length of decimal_point */ 325ebbad5ecSDavid Schultz int signflag; /* true if float is negative */ 326ebbad5ecSDavid Schultz union { /* floating point arguments %[aAeEfFgG] */ 327ebbad5ecSDavid Schultz double dbl; 328ebbad5ecSDavid Schultz long double ldbl; 329ebbad5ecSDavid Schultz } fparg; 33058f0484fSRodney W. Grimes int expt; /* integer value of exponent */ 331ebbad5ecSDavid Schultz char expchar; /* exponent character: [eEpP\0] */ 332ebbad5ecSDavid Schultz char *dtoaend; /* pointer to end of converted digits */ 33358f0484fSRodney W. Grimes int expsize; /* character count for expstr */ 334ebbad5ecSDavid Schultz int ndig; /* actual number of digits returned by dtoa */ 335ebbad5ecSDavid Schultz char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ 3362ffc61baSTor Egge char *dtoaresult; /* buffer allocated by dtoa */ 33758f0484fSRodney W. Grimes #endif 33858f0484fSRodney W. Grimes u_long ulval; /* integer arguments %[diouxX] */ 3397735bb0fSBill Fenner uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ 34058f0484fSRodney W. Grimes int base; /* base for [diouxX] conversion */ 34158f0484fSRodney W. Grimes int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 342261a532aSBill Fenner int realsz; /* field size expanded by dprec, sign, etc */ 34358f0484fSRodney W. Grimes int size; /* size of converted field or string */ 34492e88f87SAndrey A. Chernov int prsize; /* max size of printed field */ 345ebbad5ecSDavid Schultz const char *xdigs; /* digits for %[xX] conversion */ 346814d1bc9SDavid Schultz struct io_state io; /* I/O buffering state */ 34738cac8f8SDavid Schultz char buf[BUF]; /* buffer with space for digits of uintmax_t */ 348ebbad5ecSDavid Schultz char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ 349a387081cSDoug Rabson union arg *argtable; /* args, built due to positional arg */ 350a387081cSDoug Rabson union arg statargtable [STATIC_ARG_TBL_SIZE]; 351efb7e53dSJordan K. Hubbard int nextarg; /* 1-based argument index */ 352efb7e53dSJordan K. Hubbard va_list orgap; /* original argument pointer */ 353b9aac308STim J. Robbins char *convbuf; /* wide to multibyte conversion result */ 35458f0484fSRodney W. Grimes 355ac9913a7SDavid Schultz static const char xdigs_lower[16] = "0123456789abcdef"; 356ac9913a7SDavid Schultz static const char xdigs_upper[16] = "0123456789ABCDEF"; 357ebbad5ecSDavid Schultz 358814d1bc9SDavid Schultz /* BEWARE, these `goto error' on error. */ 35958f0484fSRodney W. Grimes #define PRINT(ptr, len) { \ 360814d1bc9SDavid Schultz if (io_print(&io, (ptr), (len))) \ 36158f0484fSRodney W. Grimes goto error; \ 36258f0484fSRodney W. Grimes } 36358f0484fSRodney W. Grimes #define PAD(howmany, with) { \ 364814d1bc9SDavid Schultz if (io_pad(&io, (howmany), (with))) \ 36558f0484fSRodney W. Grimes goto error; \ 366814d1bc9SDavid Schultz } 367814d1bc9SDavid Schultz #define PRINTANDPAD(p, ep, len, with) { \ 368814d1bc9SDavid Schultz if (io_printandpad(&io, (p), (ep), (len), (with))) \ 369814d1bc9SDavid Schultz goto error; \ 370814d1bc9SDavid Schultz } 371814d1bc9SDavid Schultz #define FLUSH() { \ 372814d1bc9SDavid Schultz if (io_flush(&io)) \ 373814d1bc9SDavid Schultz goto error; \ 37458f0484fSRodney W. Grimes } 37558f0484fSRodney W. Grimes 37658f0484fSRodney W. Grimes /* 377efb7e53dSJordan K. Hubbard * Get the argument indexed by nextarg. If the argument table is 378efb7e53dSJordan K. Hubbard * built, use it to get the argument. If its not, get the next 379efb7e53dSJordan K. Hubbard * argument (and arguments must be gotten sequentially). 380efb7e53dSJordan K. Hubbard */ 381efb7e53dSJordan K. Hubbard #define GETARG(type) \ 382a387081cSDoug Rabson ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ 383efb7e53dSJordan K. Hubbard (nextarg++, va_arg(ap, type))) 384efb7e53dSJordan K. Hubbard 385efb7e53dSJordan K. Hubbard /* 38658f0484fSRodney W. Grimes * To extend shorts properly, we need both signed and unsigned 38758f0484fSRodney W. Grimes * argument extraction methods. 38858f0484fSRodney W. Grimes */ 38958f0484fSRodney W. Grimes #define SARG() \ 390efb7e53dSJordan K. Hubbard (flags&LONGINT ? GETARG(long) : \ 391efb7e53dSJordan K. Hubbard flags&SHORTINT ? (long)(short)GETARG(int) : \ 3927735bb0fSBill Fenner flags&CHARINT ? (long)(signed char)GETARG(int) : \ 393efb7e53dSJordan K. Hubbard (long)GETARG(int)) 39458f0484fSRodney W. Grimes #define UARG() \ 395efb7e53dSJordan K. Hubbard (flags&LONGINT ? GETARG(u_long) : \ 396efb7e53dSJordan K. Hubbard flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 3977735bb0fSBill Fenner flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ 398efb7e53dSJordan K. Hubbard (u_long)GETARG(u_int)) 3997735bb0fSBill Fenner #define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) 4007735bb0fSBill Fenner #define SJARG() \ 4017735bb0fSBill Fenner (flags&INTMAXT ? GETARG(intmax_t) : \ 4020881683bSDavid Schultz flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ 4037735bb0fSBill Fenner flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ 4047735bb0fSBill Fenner (intmax_t)GETARG(long long)) 4057735bb0fSBill Fenner #define UJARG() \ 4067735bb0fSBill Fenner (flags&INTMAXT ? GETARG(uintmax_t) : \ 4077735bb0fSBill Fenner flags&SIZET ? (uintmax_t)GETARG(size_t) : \ 4087735bb0fSBill Fenner flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ 4097735bb0fSBill Fenner (uintmax_t)GETARG(unsigned long long)) 410efb7e53dSJordan K. Hubbard 411efb7e53dSJordan K. Hubbard /* 412efb7e53dSJordan K. Hubbard * Get * arguments, including the form *nn$. Preserve the nextarg 413efb7e53dSJordan K. Hubbard * that the argument can be gotten once the type is determined. 414efb7e53dSJordan K. Hubbard */ 415efb7e53dSJordan K. Hubbard #define GETASTER(val) \ 416efb7e53dSJordan K. Hubbard n2 = 0; \ 417efb7e53dSJordan K. Hubbard cp = fmt; \ 418efb7e53dSJordan K. Hubbard while (is_digit(*cp)) { \ 419efb7e53dSJordan K. Hubbard n2 = 10 * n2 + to_digit(*cp); \ 420efb7e53dSJordan K. Hubbard cp++; \ 421efb7e53dSJordan K. Hubbard } \ 422efb7e53dSJordan K. Hubbard if (*cp == '$') { \ 423efb7e53dSJordan K. Hubbard int hold = nextarg; \ 424efb7e53dSJordan K. Hubbard if (argtable == NULL) { \ 425efb7e53dSJordan K. Hubbard argtable = statargtable; \ 426e62e5ff9SDavid Schultz if (__find_arguments (fmt0, orgap, &argtable)) { \ 427e62e5ff9SDavid Schultz ret = EOF; \ 428e62e5ff9SDavid Schultz goto error; \ 429e62e5ff9SDavid Schultz } \ 430efb7e53dSJordan K. Hubbard } \ 431efb7e53dSJordan K. Hubbard nextarg = n2; \ 432efb7e53dSJordan K. Hubbard val = GETARG (int); \ 433efb7e53dSJordan K. Hubbard nextarg = hold; \ 434efb7e53dSJordan K. Hubbard fmt = ++cp; \ 435efb7e53dSJordan K. Hubbard } else { \ 436efb7e53dSJordan K. Hubbard val = GETARG (int); \ 437efb7e53dSJordan K. Hubbard } 438efb7e53dSJordan K. Hubbard 43933bff5d3SDavid Schultz if (__use_xprintf == 0 && getenv("USE_XPRINTF")) 44033bff5d3SDavid Schultz __use_xprintf = 1; 44133bff5d3SDavid Schultz if (__use_xprintf > 0) 44233bff5d3SDavid Schultz return (__xvprintf(fp, fmt0, ap)); 44358f0484fSRodney W. Grimes 44458f0484fSRodney W. Grimes /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 44552183d46SDavid Schultz if (prepwrite(fp) != 0) 44658f0484fSRodney W. Grimes return (EOF); 44758f0484fSRodney W. Grimes 448e18701f4SDavid Schultz convbuf = NULL; 44958f0484fSRodney W. Grimes fmt = (char *)fmt0; 450efb7e53dSJordan K. Hubbard argtable = NULL; 451efb7e53dSJordan K. Hubbard nextarg = 1; 452d07090a8STim J. Robbins va_copy(orgap, ap); 453814d1bc9SDavid Schultz io_init(&io, fp); 45458f0484fSRodney W. Grimes ret = 0; 455e18701f4SDavid Schultz #ifndef NO_FLOATING_POINT 456e18701f4SDavid Schultz dtoaresult = NULL; 457e18701f4SDavid Schultz decimal_point = localeconv()->decimal_point; 4585004a238SDavid Schultz /* The overwhelmingly common case is decpt_len == 1. */ 4595004a238SDavid Schultz decpt_len = (decimal_point[1] == '\0' ? 1 : strlen(decimal_point)); 460e18701f4SDavid Schultz #endif 46158f0484fSRodney W. Grimes 46258f0484fSRodney W. Grimes /* 46358f0484fSRodney W. Grimes * Scan the format for conversions (`%' character). 46458f0484fSRodney W. Grimes */ 46558f0484fSRodney W. Grimes for (;;) { 46658f0484fSRodney W. Grimes for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 46758f0484fSRodney W. Grimes /* void */; 46858f0484fSRodney W. Grimes if ((n = fmt - cp) != 0) { 469b250f248SAndrey A. Chernov if ((unsigned)ret + n > INT_MAX) { 47092e88f87SAndrey A. Chernov ret = EOF; 47192e88f87SAndrey A. Chernov goto error; 47292e88f87SAndrey A. Chernov } 47358f0484fSRodney W. Grimes PRINT(cp, n); 47458f0484fSRodney W. Grimes ret += n; 47558f0484fSRodney W. Grimes } 47658f0484fSRodney W. Grimes if (ch == '\0') 47758f0484fSRodney W. Grimes goto done; 47858f0484fSRodney W. Grimes fmt++; /* skip over '%' */ 47958f0484fSRodney W. Grimes 48058f0484fSRodney W. Grimes flags = 0; 48158f0484fSRodney W. Grimes dprec = 0; 48258f0484fSRodney W. Grimes width = 0; 48358f0484fSRodney W. Grimes prec = -1; 48421ca178eSDavid Schultz gs.grouping = NULL; 48558f0484fSRodney W. Grimes sign = '\0'; 486ebbad5ecSDavid Schultz ox[1] = '\0'; 48758f0484fSRodney W. Grimes 48858f0484fSRodney W. Grimes rflag: ch = *fmt++; 48958f0484fSRodney W. Grimes reswitch: switch (ch) { 49058f0484fSRodney W. Grimes case ' ': 4912e394b2fSAlexey Zelkin /*- 49258f0484fSRodney W. Grimes * ``If the space and + flags both appear, the space 49358f0484fSRodney W. Grimes * flag will be ignored.'' 49458f0484fSRodney W. Grimes * -- ANSI X3J11 49558f0484fSRodney W. Grimes */ 49658f0484fSRodney W. Grimes if (!sign) 49758f0484fSRodney W. Grimes sign = ' '; 49858f0484fSRodney W. Grimes goto rflag; 49958f0484fSRodney W. Grimes case '#': 50058f0484fSRodney W. Grimes flags |= ALT; 50158f0484fSRodney W. Grimes goto rflag; 50258f0484fSRodney W. Grimes case '*': 5032e394b2fSAlexey Zelkin /*- 50458f0484fSRodney W. Grimes * ``A negative field width argument is taken as a 50558f0484fSRodney W. Grimes * - flag followed by a positive field width.'' 50658f0484fSRodney W. Grimes * -- ANSI X3J11 50758f0484fSRodney W. Grimes * They don't exclude field widths read from args. 50858f0484fSRodney W. Grimes */ 509efb7e53dSJordan K. Hubbard GETASTER (width); 510efb7e53dSJordan K. Hubbard if (width >= 0) 51158f0484fSRodney W. Grimes goto rflag; 51258f0484fSRodney W. Grimes width = -width; 51358f0484fSRodney W. Grimes /* FALLTHROUGH */ 51458f0484fSRodney W. Grimes case '-': 51558f0484fSRodney W. Grimes flags |= LADJUST; 51658f0484fSRodney W. Grimes goto rflag; 51758f0484fSRodney W. Grimes case '+': 51858f0484fSRodney W. Grimes sign = '+'; 51958f0484fSRodney W. Grimes goto rflag; 5207735bb0fSBill Fenner case '\'': 52198ee7635SAlexey Zelkin flags |= GROUPING; 5227735bb0fSBill Fenner goto rflag; 52358f0484fSRodney W. Grimes case '.': 52458f0484fSRodney W. Grimes if ((ch = *fmt++) == '*') { 5253b204b7dSDavid Schultz GETASTER (prec); 52658f0484fSRodney W. Grimes goto rflag; 52758f0484fSRodney W. Grimes } 5283b204b7dSDavid Schultz prec = 0; 52958f0484fSRodney W. Grimes while (is_digit(ch)) { 5303b204b7dSDavid Schultz prec = 10 * prec + to_digit(ch); 53158f0484fSRodney W. Grimes ch = *fmt++; 53258f0484fSRodney W. Grimes } 53358f0484fSRodney W. Grimes goto reswitch; 53458f0484fSRodney W. Grimes case '0': 5352e394b2fSAlexey Zelkin /*- 53658f0484fSRodney W. Grimes * ``Note that 0 is taken as a flag, not as the 53758f0484fSRodney W. Grimes * beginning of a field width.'' 53858f0484fSRodney W. Grimes * -- ANSI X3J11 53958f0484fSRodney W. Grimes */ 54058f0484fSRodney W. Grimes flags |= ZEROPAD; 54158f0484fSRodney W. Grimes goto rflag; 54258f0484fSRodney W. Grimes case '1': case '2': case '3': case '4': 54358f0484fSRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 54458f0484fSRodney W. Grimes n = 0; 54558f0484fSRodney W. Grimes do { 54658f0484fSRodney W. Grimes n = 10 * n + to_digit(ch); 54758f0484fSRodney W. Grimes ch = *fmt++; 54858f0484fSRodney W. Grimes } while (is_digit(ch)); 549efb7e53dSJordan K. Hubbard if (ch == '$') { 550efb7e53dSJordan K. Hubbard nextarg = n; 551efb7e53dSJordan K. Hubbard if (argtable == NULL) { 552efb7e53dSJordan K. Hubbard argtable = statargtable; 553e62e5ff9SDavid Schultz if (__find_arguments (fmt0, orgap, 554e62e5ff9SDavid Schultz &argtable)) { 555e62e5ff9SDavid Schultz ret = EOF; 556e62e5ff9SDavid Schultz goto error; 557e62e5ff9SDavid Schultz } 558efb7e53dSJordan K. Hubbard } 559efb7e53dSJordan K. Hubbard goto rflag; 560efb7e53dSJordan K. Hubbard } 56158f0484fSRodney W. Grimes width = n; 56258f0484fSRodney W. Grimes goto reswitch; 5638de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 56458f0484fSRodney W. Grimes case 'L': 56558f0484fSRodney W. Grimes flags |= LONGDBL; 56658f0484fSRodney W. Grimes goto rflag; 56758f0484fSRodney W. Grimes #endif 56858f0484fSRodney W. Grimes case 'h': 5697735bb0fSBill Fenner if (flags & SHORTINT) { 5707735bb0fSBill Fenner flags &= ~SHORTINT; 5717735bb0fSBill Fenner flags |= CHARINT; 5727735bb0fSBill Fenner } else 57358f0484fSRodney W. Grimes flags |= SHORTINT; 57458f0484fSRodney W. Grimes goto rflag; 5757735bb0fSBill Fenner case 'j': 5767735bb0fSBill Fenner flags |= INTMAXT; 5777735bb0fSBill Fenner goto rflag; 57858f0484fSRodney W. Grimes case 'l': 5797735bb0fSBill Fenner if (flags & LONGINT) { 5807735bb0fSBill Fenner flags &= ~LONGINT; 5817735bb0fSBill Fenner flags |= LLONGINT; 5827735bb0fSBill Fenner } else 58358f0484fSRodney W. Grimes flags |= LONGINT; 58458f0484fSRodney W. Grimes goto rflag; 58558f0484fSRodney W. Grimes case 'q': 5867735bb0fSBill Fenner flags |= LLONGINT; /* not necessarily */ 5877735bb0fSBill Fenner goto rflag; 5887735bb0fSBill Fenner case 't': 5897735bb0fSBill Fenner flags |= PTRDIFFT; 5907735bb0fSBill Fenner goto rflag; 5917735bb0fSBill Fenner case 'z': 5927735bb0fSBill Fenner flags |= SIZET; 59358f0484fSRodney W. Grimes goto rflag; 594927ecbf3STim J. Robbins case 'C': 595927ecbf3STim J. Robbins flags |= LONGINT; 596927ecbf3STim J. Robbins /*FALLTHROUGH*/ 59758f0484fSRodney W. Grimes case 'c': 598b9aac308STim J. Robbins if (flags & LONGINT) { 59993996f6dSTim J. Robbins static const mbstate_t initial; 60093996f6dSTim J. Robbins mbstate_t mbs; 601b9aac308STim J. Robbins size_t mbseqlen; 602b9aac308STim J. Robbins 60393996f6dSTim J. Robbins mbs = initial; 604b9aac308STim J. Robbins mbseqlen = wcrtomb(cp = buf, 60593996f6dSTim J. Robbins (wchar_t)GETARG(wint_t), &mbs); 6066180233fSTim J. Robbins if (mbseqlen == (size_t)-1) { 6076180233fSTim J. Robbins fp->_flags |= __SERR; 608b9aac308STim J. Robbins goto error; 6096180233fSTim J. Robbins } 610b9aac308STim J. Robbins size = (int)mbseqlen; 611b9aac308STim J. Robbins } else { 612efb7e53dSJordan K. Hubbard *(cp = buf) = GETARG(int); 61358f0484fSRodney W. Grimes size = 1; 614b9aac308STim J. Robbins } 61558f0484fSRodney W. Grimes sign = '\0'; 61658f0484fSRodney W. Grimes break; 61758f0484fSRodney W. Grimes case 'D': 61858f0484fSRodney W. Grimes flags |= LONGINT; 61958f0484fSRodney W. Grimes /*FALLTHROUGH*/ 62058f0484fSRodney W. Grimes case 'd': 62158f0484fSRodney W. Grimes case 'i': 6227735bb0fSBill Fenner if (flags & INTMAX_SIZE) { 6237735bb0fSBill Fenner ujval = SJARG(); 6247735bb0fSBill Fenner if ((intmax_t)ujval < 0) { 6257735bb0fSBill Fenner ujval = -ujval; 62658f0484fSRodney W. Grimes sign = '-'; 62758f0484fSRodney W. Grimes } 62858f0484fSRodney W. Grimes } else { 62958f0484fSRodney W. Grimes ulval = SARG(); 63058f0484fSRodney W. Grimes if ((long)ulval < 0) { 63158f0484fSRodney W. Grimes ulval = -ulval; 63258f0484fSRodney W. Grimes sign = '-'; 63358f0484fSRodney W. Grimes } 63458f0484fSRodney W. Grimes } 63558f0484fSRodney W. Grimes base = 10; 63658f0484fSRodney W. Grimes goto number; 6378de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 6387735bb0fSBill Fenner case 'a': 6397735bb0fSBill Fenner case 'A': 640ebbad5ecSDavid Schultz if (ch == 'a') { 641ebbad5ecSDavid Schultz ox[1] = 'x'; 642ebbad5ecSDavid Schultz xdigs = xdigs_lower; 643ebbad5ecSDavid Schultz expchar = 'p'; 644ebbad5ecSDavid Schultz } else { 645ebbad5ecSDavid Schultz ox[1] = 'X'; 646ebbad5ecSDavid Schultz xdigs = xdigs_upper; 647ebbad5ecSDavid Schultz expchar = 'P'; 648ebbad5ecSDavid Schultz } 649904322a5SDavid Schultz if (prec >= 0) 650904322a5SDavid Schultz prec++; 651904322a5SDavid Schultz if (dtoaresult != NULL) 652904322a5SDavid Schultz freedtoa(dtoaresult); 653ebbad5ecSDavid Schultz if (flags & LONGDBL) { 654904322a5SDavid Schultz fparg.ldbl = GETARG(long double); 655ebbad5ecSDavid Schultz dtoaresult = cp = 656ebbad5ecSDavid Schultz __hldtoa(fparg.ldbl, xdigs, prec, 657ebbad5ecSDavid Schultz &expt, &signflag, &dtoaend); 658ebbad5ecSDavid Schultz } else { 659ebbad5ecSDavid Schultz fparg.dbl = GETARG(double); 660ebbad5ecSDavid Schultz dtoaresult = cp = 661ebbad5ecSDavid Schultz __hdtoa(fparg.dbl, xdigs, prec, 662ebbad5ecSDavid Schultz &expt, &signflag, &dtoaend); 663ebbad5ecSDavid Schultz } 664904322a5SDavid Schultz if (prec < 0) 665904322a5SDavid Schultz prec = dtoaend - cp; 666904322a5SDavid Schultz if (expt == INT_MAX) 667904322a5SDavid Schultz ox[1] = '\0'; 668904322a5SDavid Schultz goto fp_common; 669d26be6f0SBruce Evans case 'e': 67058f0484fSRodney W. Grimes case 'E': 671ebbad5ecSDavid Schultz expchar = ch; 672ebbad5ecSDavid Schultz if (prec < 0) /* account for digit before decpt */ 673ebbad5ecSDavid Schultz prec = DEFPREC + 1; 674ebbad5ecSDavid Schultz else 675ebbad5ecSDavid Schultz prec++; 676ebbad5ecSDavid Schultz goto fp_begin; 677d26be6f0SBruce Evans case 'f': 6787735bb0fSBill Fenner case 'F': 679ebbad5ecSDavid Schultz expchar = '\0'; 680d26be6f0SBruce Evans goto fp_begin; 68158f0484fSRodney W. Grimes case 'g': 68258f0484fSRodney W. Grimes case 'G': 683ebbad5ecSDavid Schultz expchar = ch - ('g' - 'e'); 684d26be6f0SBruce Evans if (prec == 0) 685d26be6f0SBruce Evans prec = 1; 686ebbad5ecSDavid Schultz fp_begin: 687ebbad5ecSDavid Schultz if (prec < 0) 68858f0484fSRodney W. Grimes prec = DEFPREC; 689ebbad5ecSDavid Schultz if (dtoaresult != NULL) 690ebbad5ecSDavid Schultz freedtoa(dtoaresult); 691ebbad5ecSDavid Schultz if (flags & LONGDBL) { 692ebbad5ecSDavid Schultz fparg.ldbl = GETARG(long double); 693ebbad5ecSDavid Schultz dtoaresult = cp = 694ebbad5ecSDavid Schultz __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, 695ebbad5ecSDavid Schultz &expt, &signflag, &dtoaend); 696ebbad5ecSDavid Schultz } else { 697ebbad5ecSDavid Schultz fparg.dbl = GETARG(double); 698ebbad5ecSDavid Schultz dtoaresult = cp = 699ebbad5ecSDavid Schultz dtoa(fparg.dbl, expchar ? 2 : 3, prec, 700ebbad5ecSDavid Schultz &expt, &signflag, &dtoaend); 701ebbad5ecSDavid Schultz if (expt == 9999) 702ebbad5ecSDavid Schultz expt = INT_MAX; 70358f0484fSRodney W. Grimes } 704904322a5SDavid Schultz fp_common: 705ebbad5ecSDavid Schultz if (signflag) 706ebbad5ecSDavid Schultz sign = '-'; 707ebbad5ecSDavid Schultz if (expt == INT_MAX) { /* inf or nan */ 708ebbad5ecSDavid Schultz if (*cp == 'N') { 709ebbad5ecSDavid Schultz cp = (ch >= 'a') ? "nan" : "NAN"; 710ebbad5ecSDavid Schultz sign = '\0'; 711ebbad5ecSDavid Schultz } else 712ebbad5ecSDavid Schultz cp = (ch >= 'a') ? "inf" : "INF"; 71358f0484fSRodney W. Grimes size = 3; 714970a466cSDavid Schultz flags &= ~ZEROPAD; 71558f0484fSRodney W. Grimes break; 71658f0484fSRodney W. Grimes } 71758f0484fSRodney W. Grimes flags |= FPT; 718ebbad5ecSDavid Schultz ndig = dtoaend - cp; 71958f0484fSRodney W. Grimes if (ch == 'g' || ch == 'G') { 720ebbad5ecSDavid Schultz if (expt > -4 && expt <= prec) { 721ebbad5ecSDavid Schultz /* Make %[gG] smell like %[fF] */ 722ebbad5ecSDavid Schultz expchar = '\0'; 723ebbad5ecSDavid Schultz if (flags & ALT) 724ebbad5ecSDavid Schultz prec -= expt; 72558f0484fSRodney W. Grimes else 726ebbad5ecSDavid Schultz prec = ndig - expt; 727ebbad5ecSDavid Schultz if (prec < 0) 728ebbad5ecSDavid Schultz prec = 0; 7291f2a0cdfSDavid Schultz } else { 7301f2a0cdfSDavid Schultz /* 7311f2a0cdfSDavid Schultz * Make %[gG] smell like %[eE], but 7321f2a0cdfSDavid Schultz * trim trailing zeroes if no # flag. 7331f2a0cdfSDavid Schultz */ 7341f2a0cdfSDavid Schultz if (!(flags & ALT)) 7351f2a0cdfSDavid Schultz prec = ndig; 73658f0484fSRodney W. Grimes } 737ebbad5ecSDavid Schultz } 738ebbad5ecSDavid Schultz if (expchar) { 739ebbad5ecSDavid Schultz expsize = exponent(expstr, expt - 1, expchar); 740ebbad5ecSDavid Schultz size = expsize + prec; 7413b204b7dSDavid Schultz if (prec > 1 || flags & ALT) 7425004a238SDavid Schultz size += decpt_len; 743ebbad5ecSDavid Schultz } else { 74481ae2e9aSDavid Schultz /* space for digits before decimal point */ 74581ae2e9aSDavid Schultz if (expt > 0) 74658f0484fSRodney W. Grimes size = expt; 74781ae2e9aSDavid Schultz else /* "0" */ 74881ae2e9aSDavid Schultz size = 1; 74981ae2e9aSDavid Schultz /* space for decimal pt and following digits */ 75058f0484fSRodney W. Grimes if (prec || flags & ALT) 7515004a238SDavid Schultz size += prec + decpt_len; 75221ca178eSDavid Schultz if ((flags & GROUPING) && expt > 0) 75321ca178eSDavid Schultz size += grouping_init(&gs, expt); 754ebbad5ecSDavid Schultz } 75558f0484fSRodney W. Grimes break; 7568de9e897SDavid Schultz #endif /* !NO_FLOATING_POINT */ 75758f0484fSRodney W. Grimes case 'n': 7587735bb0fSBill Fenner /* 7597735bb0fSBill Fenner * Assignment-like behavior is specified if the 7607735bb0fSBill Fenner * value overflows or is otherwise unrepresentable. 7617735bb0fSBill Fenner * C99 says to use `signed char' for %hhn conversions. 7627735bb0fSBill Fenner */ 7637735bb0fSBill Fenner if (flags & LLONGINT) 7647735bb0fSBill Fenner *GETARG(long long *) = ret; 7657735bb0fSBill Fenner else if (flags & SIZET) 7667735bb0fSBill Fenner *GETARG(ssize_t *) = (ssize_t)ret; 7677735bb0fSBill Fenner else if (flags & PTRDIFFT) 7687735bb0fSBill Fenner *GETARG(ptrdiff_t *) = ret; 7697735bb0fSBill Fenner else if (flags & INTMAXT) 7707735bb0fSBill Fenner *GETARG(intmax_t *) = ret; 77158f0484fSRodney W. Grimes else if (flags & LONGINT) 7726e690ad4SAndrey A. Chernov *GETARG(long *) = ret; 77358f0484fSRodney W. Grimes else if (flags & SHORTINT) 7746e690ad4SAndrey A. Chernov *GETARG(short *) = ret; 7757735bb0fSBill Fenner else if (flags & CHARINT) 7767735bb0fSBill Fenner *GETARG(signed char *) = ret; 77758f0484fSRodney W. Grimes else 7786e690ad4SAndrey A. Chernov *GETARG(int *) = ret; 77958f0484fSRodney W. Grimes continue; /* no output */ 78058f0484fSRodney W. Grimes case 'O': 78158f0484fSRodney W. Grimes flags |= LONGINT; 78258f0484fSRodney W. Grimes /*FALLTHROUGH*/ 78358f0484fSRodney W. Grimes case 'o': 7847735bb0fSBill Fenner if (flags & INTMAX_SIZE) 7857735bb0fSBill Fenner ujval = UJARG(); 78658f0484fSRodney W. Grimes else 78758f0484fSRodney W. Grimes ulval = UARG(); 78858f0484fSRodney W. Grimes base = 8; 78958f0484fSRodney W. Grimes goto nosign; 79058f0484fSRodney W. Grimes case 'p': 7912e394b2fSAlexey Zelkin /*- 79258f0484fSRodney W. Grimes * ``The argument shall be a pointer to void. The 79358f0484fSRodney W. Grimes * value of the pointer is converted to a sequence 79458f0484fSRodney W. Grimes * of printable characters, in an implementation- 79558f0484fSRodney W. Grimes * defined manner.'' 79658f0484fSRodney W. Grimes * -- ANSI X3J11 79758f0484fSRodney W. Grimes */ 7987735bb0fSBill Fenner ujval = (uintmax_t)(uintptr_t)GETARG(void *); 79958f0484fSRodney W. Grimes base = 16; 800ebbad5ecSDavid Schultz xdigs = xdigs_lower; 801ebbad5ecSDavid Schultz flags = flags | INTMAXT; 802ebbad5ecSDavid Schultz ox[1] = 'x'; 80358f0484fSRodney W. Grimes goto nosign; 804927ecbf3STim J. Robbins case 'S': 805927ecbf3STim J. Robbins flags |= LONGINT; 806927ecbf3STim J. Robbins /*FALLTHROUGH*/ 80758f0484fSRodney W. Grimes case 's': 808b9aac308STim J. Robbins if (flags & LONGINT) { 809b9aac308STim J. Robbins wchar_t *wcp; 810b9aac308STim J. Robbins 811b9aac308STim J. Robbins if (convbuf != NULL) 812b9aac308STim J. Robbins free(convbuf); 813b9aac308STim J. Robbins if ((wcp = GETARG(wchar_t *)) == NULL) 814b9aac308STim J. Robbins cp = "(null)"; 815b9aac308STim J. Robbins else { 816b9aac308STim J. Robbins convbuf = __wcsconv(wcp, prec); 8176180233fSTim J. Robbins if (convbuf == NULL) { 8186180233fSTim J. Robbins fp->_flags |= __SERR; 819b9aac308STim J. Robbins goto error; 8206180233fSTim J. Robbins } 821b9aac308STim J. Robbins cp = convbuf; 822b9aac308STim J. Robbins } 823b9aac308STim J. Robbins } else if ((cp = GETARG(char *)) == NULL) 82458f0484fSRodney W. Grimes cp = "(null)"; 82558f0484fSRodney W. Grimes if (prec >= 0) { 82658f0484fSRodney W. Grimes /* 82758f0484fSRodney W. Grimes * can't use strlen; can only look for the 82858f0484fSRodney W. Grimes * NUL in the first `prec' characters, and 82958f0484fSRodney W. Grimes * strlen() will go further. 83058f0484fSRodney W. Grimes */ 831ce51cf03SJames Raynard char *p = memchr(cp, 0, (size_t)prec); 83258f0484fSRodney W. Grimes 83358f0484fSRodney W. Grimes if (p != NULL) { 83458f0484fSRodney W. Grimes size = p - cp; 83558f0484fSRodney W. Grimes if (size > prec) 83658f0484fSRodney W. Grimes size = prec; 83758f0484fSRodney W. Grimes } else 83858f0484fSRodney W. Grimes size = prec; 83958f0484fSRodney W. Grimes } else 84058f0484fSRodney W. Grimes size = strlen(cp); 84158f0484fSRodney W. Grimes sign = '\0'; 84258f0484fSRodney W. Grimes break; 84358f0484fSRodney W. Grimes case 'U': 84458f0484fSRodney W. Grimes flags |= LONGINT; 84558f0484fSRodney W. Grimes /*FALLTHROUGH*/ 84658f0484fSRodney W. Grimes case 'u': 8477735bb0fSBill Fenner if (flags & INTMAX_SIZE) 8487735bb0fSBill Fenner ujval = UJARG(); 84958f0484fSRodney W. Grimes else 85058f0484fSRodney W. Grimes ulval = UARG(); 85158f0484fSRodney W. Grimes base = 10; 85258f0484fSRodney W. Grimes goto nosign; 85358f0484fSRodney W. Grimes case 'X': 854ebbad5ecSDavid Schultz xdigs = xdigs_upper; 85558f0484fSRodney W. Grimes goto hex; 85658f0484fSRodney W. Grimes case 'x': 857ebbad5ecSDavid Schultz xdigs = xdigs_lower; 8587735bb0fSBill Fenner hex: 8597735bb0fSBill Fenner if (flags & INTMAX_SIZE) 8607735bb0fSBill Fenner ujval = UJARG(); 86158f0484fSRodney W. Grimes else 86258f0484fSRodney W. Grimes ulval = UARG(); 86358f0484fSRodney W. Grimes base = 16; 86458f0484fSRodney W. Grimes /* leading 0x/X only if non-zero */ 86558f0484fSRodney W. Grimes if (flags & ALT && 8667735bb0fSBill Fenner (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) 867ebbad5ecSDavid Schultz ox[1] = ch; 86858f0484fSRodney W. Grimes 86998ee7635SAlexey Zelkin flags &= ~GROUPING; 87058f0484fSRodney W. Grimes /* unsigned conversions */ 87158f0484fSRodney W. Grimes nosign: sign = '\0'; 8722e394b2fSAlexey Zelkin /*- 87358f0484fSRodney W. Grimes * ``... diouXx conversions ... if a precision is 87458f0484fSRodney W. Grimes * specified, the 0 flag will be ignored.'' 87558f0484fSRodney W. Grimes * -- ANSI X3J11 87658f0484fSRodney W. Grimes */ 87758f0484fSRodney W. Grimes number: if ((dprec = prec) >= 0) 87858f0484fSRodney W. Grimes flags &= ~ZEROPAD; 87958f0484fSRodney W. Grimes 8802e394b2fSAlexey Zelkin /*- 88158f0484fSRodney W. Grimes * ``The result of converting a zero value with an 88258f0484fSRodney W. Grimes * explicit precision of zero is no characters.'' 88358f0484fSRodney W. Grimes * -- ANSI X3J11 8841be5319aSDavid Schultz * 8851be5319aSDavid Schultz * ``The C Standard is clear enough as is. The call 8861be5319aSDavid Schultz * printf("%#.0o", 0) should print 0.'' 8871be5319aSDavid Schultz * -- Defect Report #151 88858f0484fSRodney W. Grimes */ 88958f0484fSRodney W. Grimes cp = buf + BUF; 8907735bb0fSBill Fenner if (flags & INTMAX_SIZE) { 8911be5319aSDavid Schultz if (ujval != 0 || prec != 0 || 8921be5319aSDavid Schultz (flags & ALT && base == 8)) 8937735bb0fSBill Fenner cp = __ujtoa(ujval, cp, base, 89421ca178eSDavid Schultz flags & ALT, xdigs); 89558f0484fSRodney W. Grimes } else { 8961be5319aSDavid Schultz if (ulval != 0 || prec != 0 || 8971be5319aSDavid Schultz (flags & ALT && base == 8)) 89858f0484fSRodney W. Grimes cp = __ultoa(ulval, cp, base, 89921ca178eSDavid Schultz flags & ALT, xdigs); 90058f0484fSRodney W. Grimes } 90158f0484fSRodney W. Grimes size = buf + BUF - cp; 90238cac8f8SDavid Schultz if (size > BUF) /* should never happen */ 90338cac8f8SDavid Schultz abort(); 90421ca178eSDavid Schultz if ((flags & GROUPING) && size != 0) 90521ca178eSDavid Schultz size += grouping_init(&gs, size); 90658f0484fSRodney W. Grimes break; 90758f0484fSRodney W. Grimes default: /* "%?" prints ?, unless ? is NUL */ 90858f0484fSRodney W. Grimes if (ch == '\0') 90958f0484fSRodney W. Grimes goto done; 91058f0484fSRodney W. Grimes /* pretend it was %c with argument ch */ 91158f0484fSRodney W. Grimes cp = buf; 91258f0484fSRodney W. Grimes *cp = ch; 91358f0484fSRodney W. Grimes size = 1; 91458f0484fSRodney W. Grimes sign = '\0'; 91558f0484fSRodney W. Grimes break; 91658f0484fSRodney W. Grimes } 91758f0484fSRodney W. Grimes 91858f0484fSRodney W. Grimes /* 91958f0484fSRodney W. Grimes * All reasonable formats wind up here. At this point, `cp' 92058f0484fSRodney W. Grimes * points to a string which (if not flags&LADJUST) should be 92158f0484fSRodney W. Grimes * padded out to `width' places. If flags&ZEROPAD, it should 92258f0484fSRodney W. Grimes * first be prefixed by any sign or other prefix; otherwise, 92358f0484fSRodney W. Grimes * it should be blank padded before the prefix is emitted. 92458f0484fSRodney W. Grimes * After any left-hand padding and prefixing, emit zeroes 92558f0484fSRodney W. Grimes * required by a decimal [diouxX] precision, then print the 92658f0484fSRodney W. Grimes * string proper, then emit zeroes required by any leftover 92758f0484fSRodney W. Grimes * floating precision; finally, if LADJUST, pad with blanks. 92858f0484fSRodney W. Grimes * 92958f0484fSRodney W. Grimes * Compute actual size, so we know how much to pad. 930261a532aSBill Fenner * size excludes decimal prec; realsz includes it. 93158f0484fSRodney W. Grimes */ 932261a532aSBill Fenner realsz = dprec > size ? dprec : size; 93358f0484fSRodney W. Grimes if (sign) 934261a532aSBill Fenner realsz++; 935904322a5SDavid Schultz if (ox[1]) 936261a532aSBill Fenner realsz += 2; 93758f0484fSRodney W. Grimes 93892e88f87SAndrey A. Chernov prsize = width > realsz ? width : realsz; 939b250f248SAndrey A. Chernov if ((unsigned)ret + prsize > INT_MAX) { 94092e88f87SAndrey A. Chernov ret = EOF; 94192e88f87SAndrey A. Chernov goto error; 94292e88f87SAndrey A. Chernov } 94392e88f87SAndrey A. Chernov 94458f0484fSRodney W. Grimes /* right-adjusting blank padding */ 94558f0484fSRodney W. Grimes if ((flags & (LADJUST|ZEROPAD)) == 0) 94658f0484fSRodney W. Grimes PAD(width - realsz, blanks); 94758f0484fSRodney W. Grimes 94858f0484fSRodney W. Grimes /* prefix */ 949904322a5SDavid Schultz if (sign) 95058f0484fSRodney W. Grimes PRINT(&sign, 1); 951904322a5SDavid Schultz 952904322a5SDavid Schultz if (ox[1]) { /* ox[1] is either x, X, or \0 */ 95358f0484fSRodney W. Grimes ox[0] = '0'; 95458f0484fSRodney W. Grimes PRINT(ox, 2); 95558f0484fSRodney W. Grimes } 95658f0484fSRodney W. Grimes 95758f0484fSRodney W. Grimes /* right-adjusting zero padding */ 95858f0484fSRodney W. Grimes if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 95958f0484fSRodney W. Grimes PAD(width - realsz, zeroes); 96058f0484fSRodney W. Grimes 96158f0484fSRodney W. Grimes /* the string or number proper */ 9628de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 96358f0484fSRodney W. Grimes if ((flags & FPT) == 0) { 96421ca178eSDavid Schultz #endif 96521ca178eSDavid Schultz /* leading zeroes from decimal precision */ 96621ca178eSDavid Schultz PAD(dprec - size, zeroes); 96721ca178eSDavid Schultz if (gs.grouping) { 96821ca178eSDavid Schultz if (grouping_print(&gs, &io, cp, buf+BUF) < 0) 96921ca178eSDavid Schultz goto error; 97021ca178eSDavid Schultz } else { 97158f0484fSRodney W. Grimes PRINT(cp, size); 97221ca178eSDavid Schultz } 97321ca178eSDavid Schultz #ifndef NO_FLOATING_POINT 97458f0484fSRodney W. Grimes } else { /* glue together f_p fragments */ 975ebbad5ecSDavid Schultz if (!expchar) { /* %[fF] or sufficiently short %[gG] */ 976ebbad5ecSDavid Schultz if (expt <= 0) { 97781ae2e9aSDavid Schultz PRINT(zeroes, 1); 97881ae2e9aSDavid Schultz if (prec || flags & ALT) 9795004a238SDavid Schultz PRINT(decimal_point,decpt_len); 98058f0484fSRodney W. Grimes PAD(-expt, zeroes); 9813b204b7dSDavid Schultz /* already handled initial 0's */ 9823b204b7dSDavid Schultz prec += expt; 98358f0484fSRodney W. Grimes } else { 98421ca178eSDavid Schultz if (gs.grouping) { 98521ca178eSDavid Schultz n = grouping_print(&gs, &io, 98621ca178eSDavid Schultz cp, dtoaend); 98721ca178eSDavid Schultz if (n < 0) 98821ca178eSDavid Schultz goto error; 98921ca178eSDavid Schultz cp += n; 99021ca178eSDavid Schultz } else { 9913b204b7dSDavid Schultz PRINTANDPAD(cp, dtoaend, 99221ca178eSDavid Schultz expt, zeroes); 99321ca178eSDavid Schultz cp += expt; 994ebbad5ecSDavid Schultz } 995ebbad5ecSDavid Schultz if (prec || flags & ALT) 9965004a238SDavid Schultz PRINT(decimal_point,decpt_len); 997ebbad5ecSDavid Schultz } 9983b204b7dSDavid Schultz PRINTANDPAD(cp, dtoaend, prec, zeroes); 999ebbad5ecSDavid Schultz } else { /* %[eE] or sufficiently long %[gG] */ 10003b204b7dSDavid Schultz if (prec > 1 || flags & ALT) { 10015004a238SDavid Schultz PRINT(cp++, 1); 10025004a238SDavid Schultz PRINT(decimal_point, decpt_len); 100358f0484fSRodney W. Grimes PRINT(cp, ndig-1); 1004ebbad5ecSDavid Schultz PAD(prec - ndig, zeroes); 100558f0484fSRodney W. Grimes } else /* XeYYY */ 100658f0484fSRodney W. Grimes PRINT(cp, 1); 100758f0484fSRodney W. Grimes PRINT(expstr, expsize); 100858f0484fSRodney W. Grimes } 100958f0484fSRodney W. Grimes } 101058f0484fSRodney W. Grimes #endif 101158f0484fSRodney W. Grimes /* left-adjusting padding (always blank) */ 101258f0484fSRodney W. Grimes if (flags & LADJUST) 101358f0484fSRodney W. Grimes PAD(width - realsz, blanks); 101458f0484fSRodney W. Grimes 101558f0484fSRodney W. Grimes /* finally, adjust ret */ 101692e88f87SAndrey A. Chernov ret += prsize; 101758f0484fSRodney W. Grimes 101858f0484fSRodney W. Grimes FLUSH(); /* copy out the I/O vectors */ 101958f0484fSRodney W. Grimes } 102058f0484fSRodney W. Grimes done: 102158f0484fSRodney W. Grimes FLUSH(); 102258f0484fSRodney W. Grimes error: 1023096ad104SDag-Erling Smørgrav va_end(orgap); 10248de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 10252ffc61baSTor Egge if (dtoaresult != NULL) 1026ebbad5ecSDavid Schultz freedtoa(dtoaresult); 10272ffc61baSTor Egge #endif 1028b9aac308STim J. Robbins if (convbuf != NULL) 1029b9aac308STim J. Robbins free(convbuf); 1030f70177e7SJulian Elischer if (__sferror(fp)) 1031f70177e7SJulian Elischer ret = EOF; 1032efb7e53dSJordan K. Hubbard if ((argtable != NULL) && (argtable != statargtable)) 1033efb7e53dSJordan K. Hubbard free (argtable); 1034f70177e7SJulian Elischer return (ret); 103558f0484fSRodney W. Grimes /* NOTREACHED */ 103658f0484fSRodney W. Grimes } 103758f0484fSRodney W. Grimes 1038