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 7558f0484fSRodney W. Grimes /* 7658f0484fSRodney W. Grimes * Flush out all the vectors defined by the given uio, 7758f0484fSRodney W. Grimes * then reset it so that it can be reused. 7858f0484fSRodney W. Grimes */ 7958f0484fSRodney W. Grimes static int 80d201fe46SDaniel Eischen __sprint(FILE *fp, struct __suio *uio) 8158f0484fSRodney W. Grimes { 82d201fe46SDaniel Eischen int err; 8358f0484fSRodney W. Grimes 8458f0484fSRodney W. Grimes if (uio->uio_resid == 0) { 8558f0484fSRodney W. Grimes uio->uio_iovcnt = 0; 8658f0484fSRodney W. Grimes return (0); 8758f0484fSRodney W. Grimes } 8858f0484fSRodney W. Grimes err = __sfvwrite(fp, uio); 8958f0484fSRodney W. Grimes uio->uio_resid = 0; 9058f0484fSRodney W. Grimes uio->uio_iovcnt = 0; 9158f0484fSRodney W. Grimes return (err); 9258f0484fSRodney W. Grimes } 9358f0484fSRodney W. Grimes 9458f0484fSRodney W. Grimes /* 9558f0484fSRodney W. Grimes * Helper function for `fprintf to unbuffered unix file': creates a 9658f0484fSRodney W. Grimes * temporary buffer. We only work on write-only files; this avoids 9758f0484fSRodney W. Grimes * worries about ungetc buffers and so forth. 9858f0484fSRodney W. Grimes */ 9958f0484fSRodney W. Grimes static int 100d201fe46SDaniel Eischen __sbprintf(FILE *fp, const char *fmt, va_list ap) 10158f0484fSRodney W. Grimes { 10258f0484fSRodney W. Grimes int ret; 10358f0484fSRodney W. Grimes FILE fake; 10458f0484fSRodney W. Grimes unsigned char buf[BUFSIZ]; 10558f0484fSRodney W. Grimes 106a1805f7bSDavid Schultz /* XXX This is probably not needed. */ 107a1805f7bSDavid Schultz if (prepwrite(fp) != 0) 108a1805f7bSDavid Schultz return (EOF); 109a1805f7bSDavid Schultz 11058f0484fSRodney W. Grimes /* copy the important variables */ 11158f0484fSRodney W. Grimes fake._flags = fp->_flags & ~__SNBF; 11258f0484fSRodney W. Grimes fake._file = fp->_file; 11358f0484fSRodney W. Grimes fake._cookie = fp->_cookie; 11458f0484fSRodney W. Grimes fake._write = fp->_write; 1151e98f887SJohn Baldwin fake._orientation = fp->_orientation; 1161e98f887SJohn Baldwin fake._mbstate = fp->_mbstate; 11758f0484fSRodney W. Grimes 11858f0484fSRodney W. Grimes /* set up the buffer */ 11958f0484fSRodney W. Grimes fake._bf._base = fake._p = buf; 12058f0484fSRodney W. Grimes fake._bf._size = fake._w = sizeof(buf); 12158f0484fSRodney W. Grimes fake._lbfsize = 0; /* not actually used, but Just In Case */ 12258f0484fSRodney W. Grimes 12358f0484fSRodney W. Grimes /* do the work, then copy any error status */ 124d201fe46SDaniel Eischen ret = __vfprintf(&fake, fmt, ap); 125d201fe46SDaniel Eischen if (ret >= 0 && __fflush(&fake)) 12658f0484fSRodney W. Grimes ret = EOF; 12758f0484fSRodney W. Grimes if (fake._flags & __SERR) 12858f0484fSRodney W. Grimes fp->_flags |= __SERR; 12958f0484fSRodney W. Grimes return (ret); 13058f0484fSRodney W. Grimes } 13158f0484fSRodney W. Grimes 13258f0484fSRodney W. Grimes /* 133b9aac308STim J. Robbins * Convert a wide character string argument for the %ls format to a multibyte 134d48c77b5STim J. Robbins * string representation. If not -1, prec specifies the maximum number of 135d48c77b5STim J. Robbins * bytes to output, and also means that we can't assume that the wide char. 136d48c77b5STim J. Robbins * string ends is null-terminated. 137b9aac308STim J. Robbins */ 138b9aac308STim J. Robbins static char * 139b9aac308STim J. Robbins __wcsconv(wchar_t *wcsarg, int prec) 140b9aac308STim J. Robbins { 14193996f6dSTim J. Robbins static const mbstate_t initial; 14293996f6dSTim J. Robbins mbstate_t mbs; 143b9aac308STim J. Robbins char buf[MB_LEN_MAX]; 144b9aac308STim J. Robbins wchar_t *p; 145d48c77b5STim J. Robbins char *convbuf; 146b9aac308STim J. Robbins size_t clen, nbytes; 147b9aac308STim J. Robbins 148d48c77b5STim J. Robbins /* Allocate space for the maximum number of bytes we could output. */ 149d48c77b5STim J. Robbins if (prec < 0) { 150d48c77b5STim J. Robbins p = wcsarg; 151d48c77b5STim J. Robbins mbs = initial; 152d48c77b5STim J. Robbins nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); 153d48c77b5STim J. Robbins if (nbytes == (size_t)-1) 154d48c77b5STim J. Robbins return (NULL); 155d48c77b5STim J. Robbins } else { 156b9aac308STim J. Robbins /* 157d48c77b5STim J. Robbins * Optimisation: if the output precision is small enough, 158d48c77b5STim J. Robbins * just allocate enough memory for the maximum instead of 159d48c77b5STim J. Robbins * scanning the string. 160b9aac308STim J. Robbins */ 161d48c77b5STim J. Robbins if (prec < 128) 162d48c77b5STim J. Robbins nbytes = prec; 163d48c77b5STim J. Robbins else { 164b9aac308STim J. Robbins nbytes = 0; 165b9aac308STim J. Robbins p = wcsarg; 16693996f6dSTim J. Robbins mbs = initial; 167b9aac308STim J. Robbins for (;;) { 16893996f6dSTim J. Robbins clen = wcrtomb(buf, *p++, &mbs); 169b9aac308STim J. Robbins if (clen == 0 || clen == (size_t)-1 || 170b9aac308STim J. Robbins nbytes + clen > prec) 171b9aac308STim J. Robbins break; 172b9aac308STim J. Robbins nbytes += clen; 173b9aac308STim J. Robbins } 174d48c77b5STim J. Robbins } 175b9aac308STim J. Robbins } 176b9aac308STim J. Robbins if ((convbuf = malloc(nbytes + 1)) == NULL) 177b9aac308STim J. Robbins return (NULL); 178b9aac308STim J. Robbins 179d48c77b5STim J. Robbins /* Fill the output buffer. */ 180b9aac308STim J. Robbins p = wcsarg; 18193996f6dSTim J. Robbins mbs = initial; 182d48c77b5STim J. Robbins if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, 183d48c77b5STim J. Robbins nbytes, &mbs)) == (size_t)-1) { 1846f098a48SAndrey A. Chernov free(convbuf); 185b9aac308STim J. Robbins return (NULL); 1866f098a48SAndrey A. Chernov } 187d48c77b5STim J. Robbins convbuf[nbytes] = '\0'; 188b9aac308STim J. Robbins return (convbuf); 189b9aac308STim J. Robbins } 190b9aac308STim J. Robbins 191b9aac308STim J. Robbins /* 192d201fe46SDaniel Eischen * MT-safe version 193d201fe46SDaniel Eischen */ 194d201fe46SDaniel Eischen int 195f8418db7SRobert Drehmel vfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap) 196f8418db7SRobert Drehmel 197d201fe46SDaniel Eischen { 198d201fe46SDaniel Eischen int ret; 199d201fe46SDaniel Eischen 200d201fe46SDaniel Eischen FLOCKFILE(fp); 201a1805f7bSDavid Schultz /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 202a1805f7bSDavid Schultz if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 203a1805f7bSDavid Schultz fp->_file >= 0) 204a1805f7bSDavid Schultz ret = __sbprintf(fp, fmt0, ap); 205a1805f7bSDavid Schultz else 206d201fe46SDaniel Eischen ret = __vfprintf(fp, fmt0, ap); 207d201fe46SDaniel Eischen FUNLOCKFILE(fp); 208d201fe46SDaniel Eischen return (ret); 209d201fe46SDaniel Eischen } 210d201fe46SDaniel Eischen 21138cac8f8SDavid Schultz /* 21238cac8f8SDavid Schultz * The size of the buffer we use as scratch space for integer 21338cac8f8SDavid Schultz * conversions, among other things. Technically, we would need the 21438cac8f8SDavid Schultz * most space for base 10 conversions with thousands' grouping 21538cac8f8SDavid Schultz * characters between each pair of digits. 100 bytes is a 21638cac8f8SDavid Schultz * conservative overestimate even for a 128-bit uintmax_t. 21738cac8f8SDavid Schultz */ 21838cac8f8SDavid Schultz #define BUF 100 21938cac8f8SDavid Schultz 22058f0484fSRodney W. Grimes /* 221d201fe46SDaniel Eischen * Non-MT-safe version 222d201fe46SDaniel Eischen */ 22358f0484fSRodney W. Grimes int 224d201fe46SDaniel Eischen __vfprintf(FILE *fp, const char *fmt0, va_list ap) 22558f0484fSRodney W. Grimes { 226d201fe46SDaniel Eischen char *fmt; /* format string */ 227d201fe46SDaniel Eischen int ch; /* character from fmt */ 228d201fe46SDaniel Eischen int n, n2; /* handy integer (short term usage) */ 229d201fe46SDaniel Eischen char *cp; /* handy char pointer (short term usage) */ 230d201fe46SDaniel Eischen int flags; /* flags as above */ 23158f0484fSRodney W. Grimes int ret; /* return value accumulator */ 23258f0484fSRodney W. Grimes int width; /* width from format (%8d), or 0 */ 233ebbad5ecSDavid Schultz int prec; /* precision from format; <0 for N/A */ 23458f0484fSRodney W. Grimes char sign; /* sign prefix (' ', '+', '-', or \0) */ 23598ee7635SAlexey Zelkin char thousands_sep; /* locale specific thousands separator */ 23698ee7635SAlexey Zelkin const char *grouping; /* locale specific numeric grouping rules */ 237814d1bc9SDavid Schultz 2388de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 239ebbad5ecSDavid Schultz /* 240ebbad5ecSDavid Schultz * We can decompose the printed representation of floating 241ebbad5ecSDavid Schultz * point numbers into several parts, some of which may be empty: 242ebbad5ecSDavid Schultz * 243ebbad5ecSDavid Schultz * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ 244ebbad5ecSDavid Schultz * A B ---C--- D E F 245ebbad5ecSDavid Schultz * 246ebbad5ecSDavid Schultz * A: 'sign' holds this value if present; '\0' otherwise 247ebbad5ecSDavid Schultz * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal 248ebbad5ecSDavid Schultz * C: cp points to the string MMMNNN. Leading and trailing 249ebbad5ecSDavid Schultz * zeros are not in the string and must be added. 250ebbad5ecSDavid Schultz * D: expchar holds this character; '\0' if no exponent, e.g. %f 251ebbad5ecSDavid Schultz * F: at least two digits for decimal, at least one digit for hex 252ebbad5ecSDavid Schultz */ 2537ae5c679SAlexey Zelkin char *decimal_point; /* locale specific decimal point */ 2545004a238SDavid Schultz int decpt_len; /* length of decimal_point */ 255ebbad5ecSDavid Schultz int signflag; /* true if float is negative */ 256ebbad5ecSDavid Schultz union { /* floating point arguments %[aAeEfFgG] */ 257ebbad5ecSDavid Schultz double dbl; 258ebbad5ecSDavid Schultz long double ldbl; 259ebbad5ecSDavid Schultz } fparg; 26058f0484fSRodney W. Grimes int expt; /* integer value of exponent */ 261ebbad5ecSDavid Schultz char expchar; /* exponent character: [eEpP\0] */ 262ebbad5ecSDavid Schultz char *dtoaend; /* pointer to end of converted digits */ 26358f0484fSRodney W. Grimes int expsize; /* character count for expstr */ 264ebbad5ecSDavid Schultz int lead; /* sig figs before decimal or group sep */ 265ebbad5ecSDavid Schultz int ndig; /* actual number of digits returned by dtoa */ 266ebbad5ecSDavid Schultz char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ 2672ffc61baSTor Egge char *dtoaresult; /* buffer allocated by dtoa */ 268ebbad5ecSDavid Schultz int nseps; /* number of group separators with ' */ 269ebbad5ecSDavid Schultz int nrepeats; /* number of repeats of the last group */ 27058f0484fSRodney W. Grimes #endif 27158f0484fSRodney W. Grimes u_long ulval; /* integer arguments %[diouxX] */ 2727735bb0fSBill Fenner uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ 27358f0484fSRodney W. Grimes int base; /* base for [diouxX] conversion */ 27458f0484fSRodney W. Grimes int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 275261a532aSBill Fenner int realsz; /* field size expanded by dprec, sign, etc */ 27658f0484fSRodney W. Grimes int size; /* size of converted field or string */ 27792e88f87SAndrey A. Chernov int prsize; /* max size of printed field */ 278ebbad5ecSDavid Schultz const char *xdigs; /* digits for %[xX] conversion */ 279814d1bc9SDavid Schultz struct io_state io; /* I/O buffering state */ 28038cac8f8SDavid Schultz char buf[BUF]; /* buffer with space for digits of uintmax_t */ 281ebbad5ecSDavid Schultz char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ 282a387081cSDoug Rabson union arg *argtable; /* args, built due to positional arg */ 283a387081cSDoug Rabson union arg statargtable [STATIC_ARG_TBL_SIZE]; 284efb7e53dSJordan K. Hubbard int nextarg; /* 1-based argument index */ 285efb7e53dSJordan K. Hubbard va_list orgap; /* original argument pointer */ 286b9aac308STim J. Robbins char *convbuf; /* wide to multibyte conversion result */ 28758f0484fSRodney W. Grimes 288ac9913a7SDavid Schultz static const char xdigs_lower[16] = "0123456789abcdef"; 289ac9913a7SDavid Schultz static const char xdigs_upper[16] = "0123456789ABCDEF"; 290ebbad5ecSDavid Schultz 291814d1bc9SDavid Schultz /* BEWARE, these `goto error' on error. */ 29258f0484fSRodney W. Grimes #define PRINT(ptr, len) { \ 293814d1bc9SDavid Schultz if (io_print(&io, (ptr), (len))) \ 29458f0484fSRodney W. Grimes goto error; \ 29558f0484fSRodney W. Grimes } 29658f0484fSRodney W. Grimes #define PAD(howmany, with) { \ 297814d1bc9SDavid Schultz if (io_pad(&io, (howmany), (with))) \ 29858f0484fSRodney W. Grimes goto error; \ 299814d1bc9SDavid Schultz } 300814d1bc9SDavid Schultz #define PRINTANDPAD(p, ep, len, with) { \ 301814d1bc9SDavid Schultz if (io_printandpad(&io, (p), (ep), (len), (with))) \ 302814d1bc9SDavid Schultz goto error; \ 303814d1bc9SDavid Schultz } 304814d1bc9SDavid Schultz #define FLUSH() { \ 305814d1bc9SDavid Schultz if (io_flush(&io)) \ 306814d1bc9SDavid Schultz goto error; \ 30758f0484fSRodney W. Grimes } 30858f0484fSRodney W. Grimes 30958f0484fSRodney W. Grimes /* 310efb7e53dSJordan K. Hubbard * Get the argument indexed by nextarg. If the argument table is 311efb7e53dSJordan K. Hubbard * built, use it to get the argument. If its not, get the next 312efb7e53dSJordan K. Hubbard * argument (and arguments must be gotten sequentially). 313efb7e53dSJordan K. Hubbard */ 314efb7e53dSJordan K. Hubbard #define GETARG(type) \ 315a387081cSDoug Rabson ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ 316efb7e53dSJordan K. Hubbard (nextarg++, va_arg(ap, type))) 317efb7e53dSJordan K. Hubbard 318efb7e53dSJordan K. Hubbard /* 31958f0484fSRodney W. Grimes * To extend shorts properly, we need both signed and unsigned 32058f0484fSRodney W. Grimes * argument extraction methods. 32158f0484fSRodney W. Grimes */ 32258f0484fSRodney W. Grimes #define SARG() \ 323efb7e53dSJordan K. Hubbard (flags&LONGINT ? GETARG(long) : \ 324efb7e53dSJordan K. Hubbard flags&SHORTINT ? (long)(short)GETARG(int) : \ 3257735bb0fSBill Fenner flags&CHARINT ? (long)(signed char)GETARG(int) : \ 326efb7e53dSJordan K. Hubbard (long)GETARG(int)) 32758f0484fSRodney W. Grimes #define UARG() \ 328efb7e53dSJordan K. Hubbard (flags&LONGINT ? GETARG(u_long) : \ 329efb7e53dSJordan K. Hubbard flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 3307735bb0fSBill Fenner flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ 331efb7e53dSJordan K. Hubbard (u_long)GETARG(u_int)) 3327735bb0fSBill Fenner #define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) 3337735bb0fSBill Fenner #define SJARG() \ 3347735bb0fSBill Fenner (flags&INTMAXT ? GETARG(intmax_t) : \ 3357735bb0fSBill Fenner flags&SIZET ? (intmax_t)GETARG(size_t) : \ 3367735bb0fSBill Fenner flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ 3377735bb0fSBill Fenner (intmax_t)GETARG(long long)) 3387735bb0fSBill Fenner #define UJARG() \ 3397735bb0fSBill Fenner (flags&INTMAXT ? GETARG(uintmax_t) : \ 3407735bb0fSBill Fenner flags&SIZET ? (uintmax_t)GETARG(size_t) : \ 3417735bb0fSBill Fenner flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ 3427735bb0fSBill Fenner (uintmax_t)GETARG(unsigned long long)) 343efb7e53dSJordan K. Hubbard 344efb7e53dSJordan K. Hubbard /* 345efb7e53dSJordan K. Hubbard * Get * arguments, including the form *nn$. Preserve the nextarg 346efb7e53dSJordan K. Hubbard * that the argument can be gotten once the type is determined. 347efb7e53dSJordan K. Hubbard */ 348efb7e53dSJordan K. Hubbard #define GETASTER(val) \ 349efb7e53dSJordan K. Hubbard n2 = 0; \ 350efb7e53dSJordan K. Hubbard cp = fmt; \ 351efb7e53dSJordan K. Hubbard while (is_digit(*cp)) { \ 352efb7e53dSJordan K. Hubbard n2 = 10 * n2 + to_digit(*cp); \ 353efb7e53dSJordan K. Hubbard cp++; \ 354efb7e53dSJordan K. Hubbard } \ 355efb7e53dSJordan K. Hubbard if (*cp == '$') { \ 356efb7e53dSJordan K. Hubbard int hold = nextarg; \ 357efb7e53dSJordan K. Hubbard if (argtable == NULL) { \ 358efb7e53dSJordan K. Hubbard argtable = statargtable; \ 359e62e5ff9SDavid Schultz if (__find_arguments (fmt0, orgap, &argtable)) { \ 360e62e5ff9SDavid Schultz ret = EOF; \ 361e62e5ff9SDavid Schultz goto error; \ 362e62e5ff9SDavid Schultz } \ 363efb7e53dSJordan K. Hubbard } \ 364efb7e53dSJordan K. Hubbard nextarg = n2; \ 365efb7e53dSJordan K. Hubbard val = GETARG (int); \ 366efb7e53dSJordan K. Hubbard nextarg = hold; \ 367efb7e53dSJordan K. Hubbard fmt = ++cp; \ 368efb7e53dSJordan K. Hubbard } else { \ 369efb7e53dSJordan K. Hubbard val = GETARG (int); \ 370efb7e53dSJordan K. Hubbard } 371efb7e53dSJordan K. Hubbard 37233bff5d3SDavid Schultz if (__use_xprintf == 0 && getenv("USE_XPRINTF")) 37333bff5d3SDavid Schultz __use_xprintf = 1; 37433bff5d3SDavid Schultz if (__use_xprintf > 0) 37533bff5d3SDavid Schultz return (__xvprintf(fp, fmt0, ap)); 37658f0484fSRodney W. Grimes 37758f0484fSRodney W. Grimes /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 37852183d46SDavid Schultz if (prepwrite(fp) != 0) 37958f0484fSRodney W. Grimes return (EOF); 38058f0484fSRodney W. Grimes 381e18701f4SDavid Schultz thousands_sep = '\0'; 382e18701f4SDavid Schultz grouping = NULL; 383e18701f4SDavid Schultz convbuf = NULL; 38458f0484fSRodney W. Grimes fmt = (char *)fmt0; 385efb7e53dSJordan K. Hubbard argtable = NULL; 386efb7e53dSJordan K. Hubbard nextarg = 1; 387d07090a8STim J. Robbins va_copy(orgap, ap); 388814d1bc9SDavid Schultz io_init(&io, fp); 38958f0484fSRodney W. Grimes ret = 0; 390e18701f4SDavid Schultz #ifndef NO_FLOATING_POINT 391e18701f4SDavid Schultz dtoaresult = NULL; 392e18701f4SDavid Schultz decimal_point = localeconv()->decimal_point; 3935004a238SDavid Schultz /* The overwhelmingly common case is decpt_len == 1. */ 3945004a238SDavid Schultz decpt_len = (decimal_point[1] == '\0' ? 1 : strlen(decimal_point)); 395e18701f4SDavid Schultz #endif 39658f0484fSRodney W. Grimes 39758f0484fSRodney W. Grimes /* 39858f0484fSRodney W. Grimes * Scan the format for conversions (`%' character). 39958f0484fSRodney W. Grimes */ 40058f0484fSRodney W. Grimes for (;;) { 40158f0484fSRodney W. Grimes for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 40258f0484fSRodney W. Grimes /* void */; 40358f0484fSRodney W. Grimes if ((n = fmt - cp) != 0) { 404b250f248SAndrey A. Chernov if ((unsigned)ret + n > INT_MAX) { 40592e88f87SAndrey A. Chernov ret = EOF; 40692e88f87SAndrey A. Chernov goto error; 40792e88f87SAndrey A. Chernov } 40858f0484fSRodney W. Grimes PRINT(cp, n); 40958f0484fSRodney W. Grimes ret += n; 41058f0484fSRodney W. Grimes } 41158f0484fSRodney W. Grimes if (ch == '\0') 41258f0484fSRodney W. Grimes goto done; 41358f0484fSRodney W. Grimes fmt++; /* skip over '%' */ 41458f0484fSRodney W. Grimes 41558f0484fSRodney W. Grimes flags = 0; 41658f0484fSRodney W. Grimes dprec = 0; 41758f0484fSRodney W. Grimes width = 0; 41858f0484fSRodney W. Grimes prec = -1; 41958f0484fSRodney W. Grimes sign = '\0'; 420ebbad5ecSDavid Schultz ox[1] = '\0'; 42158f0484fSRodney W. Grimes 42258f0484fSRodney W. Grimes rflag: ch = *fmt++; 42358f0484fSRodney W. Grimes reswitch: switch (ch) { 42458f0484fSRodney W. Grimes case ' ': 4252e394b2fSAlexey Zelkin /*- 42658f0484fSRodney W. Grimes * ``If the space and + flags both appear, the space 42758f0484fSRodney W. Grimes * flag will be ignored.'' 42858f0484fSRodney W. Grimes * -- ANSI X3J11 42958f0484fSRodney W. Grimes */ 43058f0484fSRodney W. Grimes if (!sign) 43158f0484fSRodney W. Grimes sign = ' '; 43258f0484fSRodney W. Grimes goto rflag; 43358f0484fSRodney W. Grimes case '#': 43458f0484fSRodney W. Grimes flags |= ALT; 43558f0484fSRodney W. Grimes goto rflag; 43658f0484fSRodney W. Grimes case '*': 4372e394b2fSAlexey Zelkin /*- 43858f0484fSRodney W. Grimes * ``A negative field width argument is taken as a 43958f0484fSRodney W. Grimes * - flag followed by a positive field width.'' 44058f0484fSRodney W. Grimes * -- ANSI X3J11 44158f0484fSRodney W. Grimes * They don't exclude field widths read from args. 44258f0484fSRodney W. Grimes */ 443efb7e53dSJordan K. Hubbard GETASTER (width); 444efb7e53dSJordan K. Hubbard if (width >= 0) 44558f0484fSRodney W. Grimes goto rflag; 44658f0484fSRodney W. Grimes width = -width; 44758f0484fSRodney W. Grimes /* FALLTHROUGH */ 44858f0484fSRodney W. Grimes case '-': 44958f0484fSRodney W. Grimes flags |= LADJUST; 45058f0484fSRodney W. Grimes goto rflag; 45158f0484fSRodney W. Grimes case '+': 45258f0484fSRodney W. Grimes sign = '+'; 45358f0484fSRodney W. Grimes goto rflag; 4547735bb0fSBill Fenner case '\'': 45598ee7635SAlexey Zelkin flags |= GROUPING; 45698ee7635SAlexey Zelkin thousands_sep = *(localeconv()->thousands_sep); 45798ee7635SAlexey Zelkin grouping = localeconv()->grouping; 4587735bb0fSBill Fenner goto rflag; 45958f0484fSRodney W. Grimes case '.': 46058f0484fSRodney W. Grimes if ((ch = *fmt++) == '*') { 4613b204b7dSDavid Schultz GETASTER (prec); 46258f0484fSRodney W. Grimes goto rflag; 46358f0484fSRodney W. Grimes } 4643b204b7dSDavid Schultz prec = 0; 46558f0484fSRodney W. Grimes while (is_digit(ch)) { 4663b204b7dSDavid Schultz prec = 10 * prec + to_digit(ch); 46758f0484fSRodney W. Grimes ch = *fmt++; 46858f0484fSRodney W. Grimes } 46958f0484fSRodney W. Grimes goto reswitch; 47058f0484fSRodney W. Grimes case '0': 4712e394b2fSAlexey Zelkin /*- 47258f0484fSRodney W. Grimes * ``Note that 0 is taken as a flag, not as the 47358f0484fSRodney W. Grimes * beginning of a field width.'' 47458f0484fSRodney W. Grimes * -- ANSI X3J11 47558f0484fSRodney W. Grimes */ 47658f0484fSRodney W. Grimes flags |= ZEROPAD; 47758f0484fSRodney W. Grimes goto rflag; 47858f0484fSRodney W. Grimes case '1': case '2': case '3': case '4': 47958f0484fSRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 48058f0484fSRodney W. Grimes n = 0; 48158f0484fSRodney W. Grimes do { 48258f0484fSRodney W. Grimes n = 10 * n + to_digit(ch); 48358f0484fSRodney W. Grimes ch = *fmt++; 48458f0484fSRodney W. Grimes } while (is_digit(ch)); 485efb7e53dSJordan K. Hubbard if (ch == '$') { 486efb7e53dSJordan K. Hubbard nextarg = n; 487efb7e53dSJordan K. Hubbard if (argtable == NULL) { 488efb7e53dSJordan K. Hubbard argtable = statargtable; 489e62e5ff9SDavid Schultz if (__find_arguments (fmt0, orgap, 490e62e5ff9SDavid Schultz &argtable)) { 491e62e5ff9SDavid Schultz ret = EOF; 492e62e5ff9SDavid Schultz goto error; 493e62e5ff9SDavid Schultz } 494efb7e53dSJordan K. Hubbard } 495efb7e53dSJordan K. Hubbard goto rflag; 496efb7e53dSJordan K. Hubbard } 49758f0484fSRodney W. Grimes width = n; 49858f0484fSRodney W. Grimes goto reswitch; 4998de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 50058f0484fSRodney W. Grimes case 'L': 50158f0484fSRodney W. Grimes flags |= LONGDBL; 50258f0484fSRodney W. Grimes goto rflag; 50358f0484fSRodney W. Grimes #endif 50458f0484fSRodney W. Grimes case 'h': 5057735bb0fSBill Fenner if (flags & SHORTINT) { 5067735bb0fSBill Fenner flags &= ~SHORTINT; 5077735bb0fSBill Fenner flags |= CHARINT; 5087735bb0fSBill Fenner } else 50958f0484fSRodney W. Grimes flags |= SHORTINT; 51058f0484fSRodney W. Grimes goto rflag; 5117735bb0fSBill Fenner case 'j': 5127735bb0fSBill Fenner flags |= INTMAXT; 5137735bb0fSBill Fenner goto rflag; 51458f0484fSRodney W. Grimes case 'l': 5157735bb0fSBill Fenner if (flags & LONGINT) { 5167735bb0fSBill Fenner flags &= ~LONGINT; 5177735bb0fSBill Fenner flags |= LLONGINT; 5187735bb0fSBill Fenner } else 51958f0484fSRodney W. Grimes flags |= LONGINT; 52058f0484fSRodney W. Grimes goto rflag; 52158f0484fSRodney W. Grimes case 'q': 5227735bb0fSBill Fenner flags |= LLONGINT; /* not necessarily */ 5237735bb0fSBill Fenner goto rflag; 5247735bb0fSBill Fenner case 't': 5257735bb0fSBill Fenner flags |= PTRDIFFT; 5267735bb0fSBill Fenner goto rflag; 5277735bb0fSBill Fenner case 'z': 5287735bb0fSBill Fenner flags |= SIZET; 52958f0484fSRodney W. Grimes goto rflag; 530927ecbf3STim J. Robbins case 'C': 531927ecbf3STim J. Robbins flags |= LONGINT; 532927ecbf3STim J. Robbins /*FALLTHROUGH*/ 53358f0484fSRodney W. Grimes case 'c': 534b9aac308STim J. Robbins if (flags & LONGINT) { 53593996f6dSTim J. Robbins static const mbstate_t initial; 53693996f6dSTim J. Robbins mbstate_t mbs; 537b9aac308STim J. Robbins size_t mbseqlen; 538b9aac308STim J. Robbins 53993996f6dSTim J. Robbins mbs = initial; 540b9aac308STim J. Robbins mbseqlen = wcrtomb(cp = buf, 54193996f6dSTim J. Robbins (wchar_t)GETARG(wint_t), &mbs); 5426180233fSTim J. Robbins if (mbseqlen == (size_t)-1) { 5436180233fSTim J. Robbins fp->_flags |= __SERR; 544b9aac308STim J. Robbins goto error; 5456180233fSTim J. Robbins } 546b9aac308STim J. Robbins size = (int)mbseqlen; 547b9aac308STim J. Robbins } else { 548efb7e53dSJordan K. Hubbard *(cp = buf) = GETARG(int); 54958f0484fSRodney W. Grimes size = 1; 550b9aac308STim J. Robbins } 55158f0484fSRodney W. Grimes sign = '\0'; 55258f0484fSRodney W. Grimes break; 55358f0484fSRodney W. Grimes case 'D': 55458f0484fSRodney W. Grimes flags |= LONGINT; 55558f0484fSRodney W. Grimes /*FALLTHROUGH*/ 55658f0484fSRodney W. Grimes case 'd': 55758f0484fSRodney W. Grimes case 'i': 5587735bb0fSBill Fenner if (flags & INTMAX_SIZE) { 5597735bb0fSBill Fenner ujval = SJARG(); 5607735bb0fSBill Fenner if ((intmax_t)ujval < 0) { 5617735bb0fSBill Fenner ujval = -ujval; 56258f0484fSRodney W. Grimes sign = '-'; 56358f0484fSRodney W. Grimes } 56458f0484fSRodney W. Grimes } else { 56558f0484fSRodney W. Grimes ulval = SARG(); 56658f0484fSRodney W. Grimes if ((long)ulval < 0) { 56758f0484fSRodney W. Grimes ulval = -ulval; 56858f0484fSRodney W. Grimes sign = '-'; 56958f0484fSRodney W. Grimes } 57058f0484fSRodney W. Grimes } 57158f0484fSRodney W. Grimes base = 10; 57258f0484fSRodney W. Grimes goto number; 5738de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 5747735bb0fSBill Fenner case 'a': 5757735bb0fSBill Fenner case 'A': 576ebbad5ecSDavid Schultz if (ch == 'a') { 577ebbad5ecSDavid Schultz ox[1] = 'x'; 578ebbad5ecSDavid Schultz xdigs = xdigs_lower; 579ebbad5ecSDavid Schultz expchar = 'p'; 580ebbad5ecSDavid Schultz } else { 581ebbad5ecSDavid Schultz ox[1] = 'X'; 582ebbad5ecSDavid Schultz xdigs = xdigs_upper; 583ebbad5ecSDavid Schultz expchar = 'P'; 584ebbad5ecSDavid Schultz } 585904322a5SDavid Schultz if (prec >= 0) 586904322a5SDavid Schultz prec++; 587904322a5SDavid Schultz if (dtoaresult != NULL) 588904322a5SDavid Schultz freedtoa(dtoaresult); 589ebbad5ecSDavid Schultz if (flags & LONGDBL) { 590904322a5SDavid Schultz fparg.ldbl = GETARG(long double); 591ebbad5ecSDavid Schultz dtoaresult = cp = 592ebbad5ecSDavid Schultz __hldtoa(fparg.ldbl, xdigs, prec, 593ebbad5ecSDavid Schultz &expt, &signflag, &dtoaend); 594ebbad5ecSDavid Schultz } else { 595ebbad5ecSDavid Schultz fparg.dbl = GETARG(double); 596ebbad5ecSDavid Schultz dtoaresult = cp = 597ebbad5ecSDavid Schultz __hdtoa(fparg.dbl, xdigs, prec, 598ebbad5ecSDavid Schultz &expt, &signflag, &dtoaend); 599ebbad5ecSDavid Schultz } 600904322a5SDavid Schultz if (prec < 0) 601904322a5SDavid Schultz prec = dtoaend - cp; 602904322a5SDavid Schultz if (expt == INT_MAX) 603904322a5SDavid Schultz ox[1] = '\0'; 604904322a5SDavid Schultz goto fp_common; 605d26be6f0SBruce Evans case 'e': 60658f0484fSRodney W. Grimes case 'E': 607ebbad5ecSDavid Schultz expchar = ch; 608ebbad5ecSDavid Schultz if (prec < 0) /* account for digit before decpt */ 609ebbad5ecSDavid Schultz prec = DEFPREC + 1; 610ebbad5ecSDavid Schultz else 611ebbad5ecSDavid Schultz prec++; 612ebbad5ecSDavid Schultz goto fp_begin; 613d26be6f0SBruce Evans case 'f': 6147735bb0fSBill Fenner case 'F': 615ebbad5ecSDavid Schultz expchar = '\0'; 616d26be6f0SBruce Evans goto fp_begin; 61758f0484fSRodney W. Grimes case 'g': 61858f0484fSRodney W. Grimes case 'G': 619ebbad5ecSDavid Schultz expchar = ch - ('g' - 'e'); 620d26be6f0SBruce Evans if (prec == 0) 621d26be6f0SBruce Evans prec = 1; 622ebbad5ecSDavid Schultz fp_begin: 623ebbad5ecSDavid Schultz if (prec < 0) 62458f0484fSRodney W. Grimes prec = DEFPREC; 625ebbad5ecSDavid Schultz if (dtoaresult != NULL) 626ebbad5ecSDavid Schultz freedtoa(dtoaresult); 627ebbad5ecSDavid Schultz if (flags & LONGDBL) { 628ebbad5ecSDavid Schultz fparg.ldbl = GETARG(long double); 629ebbad5ecSDavid Schultz dtoaresult = cp = 630ebbad5ecSDavid Schultz __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, 631ebbad5ecSDavid Schultz &expt, &signflag, &dtoaend); 632ebbad5ecSDavid Schultz } else { 633ebbad5ecSDavid Schultz fparg.dbl = GETARG(double); 634ebbad5ecSDavid Schultz dtoaresult = cp = 635ebbad5ecSDavid Schultz dtoa(fparg.dbl, expchar ? 2 : 3, prec, 636ebbad5ecSDavid Schultz &expt, &signflag, &dtoaend); 637ebbad5ecSDavid Schultz if (expt == 9999) 638ebbad5ecSDavid Schultz expt = INT_MAX; 63958f0484fSRodney W. Grimes } 640904322a5SDavid Schultz fp_common: 641ebbad5ecSDavid Schultz if (signflag) 642ebbad5ecSDavid Schultz sign = '-'; 643ebbad5ecSDavid Schultz if (expt == INT_MAX) { /* inf or nan */ 644ebbad5ecSDavid Schultz if (*cp == 'N') { 645ebbad5ecSDavid Schultz cp = (ch >= 'a') ? "nan" : "NAN"; 646ebbad5ecSDavid Schultz sign = '\0'; 647ebbad5ecSDavid Schultz } else 648ebbad5ecSDavid Schultz cp = (ch >= 'a') ? "inf" : "INF"; 64958f0484fSRodney W. Grimes size = 3; 650970a466cSDavid Schultz flags &= ~ZEROPAD; 65158f0484fSRodney W. Grimes break; 65258f0484fSRodney W. Grimes } 65358f0484fSRodney W. Grimes flags |= FPT; 654ebbad5ecSDavid Schultz ndig = dtoaend - cp; 65558f0484fSRodney W. Grimes if (ch == 'g' || ch == 'G') { 656ebbad5ecSDavid Schultz if (expt > -4 && expt <= prec) { 657ebbad5ecSDavid Schultz /* Make %[gG] smell like %[fF] */ 658ebbad5ecSDavid Schultz expchar = '\0'; 659ebbad5ecSDavid Schultz if (flags & ALT) 660ebbad5ecSDavid Schultz prec -= expt; 66158f0484fSRodney W. Grimes else 662ebbad5ecSDavid Schultz prec = ndig - expt; 663ebbad5ecSDavid Schultz if (prec < 0) 664ebbad5ecSDavid Schultz prec = 0; 6651f2a0cdfSDavid Schultz } else { 6661f2a0cdfSDavid Schultz /* 6671f2a0cdfSDavid Schultz * Make %[gG] smell like %[eE], but 6681f2a0cdfSDavid Schultz * trim trailing zeroes if no # flag. 6691f2a0cdfSDavid Schultz */ 6701f2a0cdfSDavid Schultz if (!(flags & ALT)) 6711f2a0cdfSDavid Schultz prec = ndig; 67258f0484fSRodney W. Grimes } 673ebbad5ecSDavid Schultz } 674ebbad5ecSDavid Schultz if (expchar) { 675ebbad5ecSDavid Schultz expsize = exponent(expstr, expt - 1, expchar); 676ebbad5ecSDavid Schultz size = expsize + prec; 6773b204b7dSDavid Schultz if (prec > 1 || flags & ALT) 6785004a238SDavid Schultz size += decpt_len; 679ebbad5ecSDavid Schultz } else { 68081ae2e9aSDavid Schultz /* space for digits before decimal point */ 68181ae2e9aSDavid Schultz if (expt > 0) 68258f0484fSRodney W. Grimes size = expt; 68381ae2e9aSDavid Schultz else /* "0" */ 68481ae2e9aSDavid Schultz size = 1; 68581ae2e9aSDavid Schultz /* space for decimal pt and following digits */ 68658f0484fSRodney W. Grimes if (prec || flags & ALT) 6875004a238SDavid Schultz size += prec + decpt_len; 688ebbad5ecSDavid Schultz if (grouping && expt > 0) { 689ebbad5ecSDavid Schultz /* space for thousands' grouping */ 690ebbad5ecSDavid Schultz nseps = nrepeats = 0; 691ebbad5ecSDavid Schultz lead = expt; 692ebbad5ecSDavid Schultz while (*grouping != CHAR_MAX) { 693ebbad5ecSDavid Schultz if (lead <= *grouping) 694ebbad5ecSDavid Schultz break; 695ebbad5ecSDavid Schultz lead -= *grouping; 696ebbad5ecSDavid Schultz if (*(grouping+1)) { 697ebbad5ecSDavid Schultz nseps++; 698ebbad5ecSDavid Schultz grouping++; 69958f0484fSRodney W. Grimes } else 700ebbad5ecSDavid Schultz nrepeats++; 701ebbad5ecSDavid Schultz } 702ebbad5ecSDavid Schultz size += nseps + nrepeats; 703ebbad5ecSDavid Schultz } else 704d890afb8SDavid Schultz lead = expt; 705ebbad5ecSDavid Schultz } 70658f0484fSRodney W. Grimes break; 7078de9e897SDavid Schultz #endif /* !NO_FLOATING_POINT */ 70858f0484fSRodney W. Grimes case 'n': 7097735bb0fSBill Fenner /* 7107735bb0fSBill Fenner * Assignment-like behavior is specified if the 7117735bb0fSBill Fenner * value overflows or is otherwise unrepresentable. 7127735bb0fSBill Fenner * C99 says to use `signed char' for %hhn conversions. 7137735bb0fSBill Fenner */ 7147735bb0fSBill Fenner if (flags & LLONGINT) 7157735bb0fSBill Fenner *GETARG(long long *) = ret; 7167735bb0fSBill Fenner else if (flags & SIZET) 7177735bb0fSBill Fenner *GETARG(ssize_t *) = (ssize_t)ret; 7187735bb0fSBill Fenner else if (flags & PTRDIFFT) 7197735bb0fSBill Fenner *GETARG(ptrdiff_t *) = ret; 7207735bb0fSBill Fenner else if (flags & INTMAXT) 7217735bb0fSBill Fenner *GETARG(intmax_t *) = ret; 72258f0484fSRodney W. Grimes else if (flags & LONGINT) 7236e690ad4SAndrey A. Chernov *GETARG(long *) = ret; 72458f0484fSRodney W. Grimes else if (flags & SHORTINT) 7256e690ad4SAndrey A. Chernov *GETARG(short *) = ret; 7267735bb0fSBill Fenner else if (flags & CHARINT) 7277735bb0fSBill Fenner *GETARG(signed char *) = ret; 72858f0484fSRodney W. Grimes else 7296e690ad4SAndrey A. Chernov *GETARG(int *) = ret; 73058f0484fSRodney W. Grimes continue; /* no output */ 73158f0484fSRodney W. Grimes case 'O': 73258f0484fSRodney W. Grimes flags |= LONGINT; 73358f0484fSRodney W. Grimes /*FALLTHROUGH*/ 73458f0484fSRodney W. Grimes case 'o': 7357735bb0fSBill Fenner if (flags & INTMAX_SIZE) 7367735bb0fSBill Fenner ujval = UJARG(); 73758f0484fSRodney W. Grimes else 73858f0484fSRodney W. Grimes ulval = UARG(); 73958f0484fSRodney W. Grimes base = 8; 74058f0484fSRodney W. Grimes goto nosign; 74158f0484fSRodney W. Grimes case 'p': 7422e394b2fSAlexey Zelkin /*- 74358f0484fSRodney W. Grimes * ``The argument shall be a pointer to void. The 74458f0484fSRodney W. Grimes * value of the pointer is converted to a sequence 74558f0484fSRodney W. Grimes * of printable characters, in an implementation- 74658f0484fSRodney W. Grimes * defined manner.'' 74758f0484fSRodney W. Grimes * -- ANSI X3J11 74858f0484fSRodney W. Grimes */ 7497735bb0fSBill Fenner ujval = (uintmax_t)(uintptr_t)GETARG(void *); 75058f0484fSRodney W. Grimes base = 16; 751ebbad5ecSDavid Schultz xdigs = xdigs_lower; 752ebbad5ecSDavid Schultz flags = flags | INTMAXT; 753ebbad5ecSDavid Schultz ox[1] = 'x'; 75458f0484fSRodney W. Grimes goto nosign; 755927ecbf3STim J. Robbins case 'S': 756927ecbf3STim J. Robbins flags |= LONGINT; 757927ecbf3STim J. Robbins /*FALLTHROUGH*/ 75858f0484fSRodney W. Grimes case 's': 759b9aac308STim J. Robbins if (flags & LONGINT) { 760b9aac308STim J. Robbins wchar_t *wcp; 761b9aac308STim J. Robbins 762b9aac308STim J. Robbins if (convbuf != NULL) 763b9aac308STim J. Robbins free(convbuf); 764b9aac308STim J. Robbins if ((wcp = GETARG(wchar_t *)) == NULL) 765b9aac308STim J. Robbins cp = "(null)"; 766b9aac308STim J. Robbins else { 767b9aac308STim J. Robbins convbuf = __wcsconv(wcp, prec); 7686180233fSTim J. Robbins if (convbuf == NULL) { 7696180233fSTim J. Robbins fp->_flags |= __SERR; 770b9aac308STim J. Robbins goto error; 7716180233fSTim J. Robbins } 772b9aac308STim J. Robbins cp = convbuf; 773b9aac308STim J. Robbins } 774b9aac308STim J. Robbins } else if ((cp = GETARG(char *)) == NULL) 77558f0484fSRodney W. Grimes cp = "(null)"; 77658f0484fSRodney W. Grimes if (prec >= 0) { 77758f0484fSRodney W. Grimes /* 77858f0484fSRodney W. Grimes * can't use strlen; can only look for the 77958f0484fSRodney W. Grimes * NUL in the first `prec' characters, and 78058f0484fSRodney W. Grimes * strlen() will go further. 78158f0484fSRodney W. Grimes */ 782ce51cf03SJames Raynard char *p = memchr(cp, 0, (size_t)prec); 78358f0484fSRodney W. Grimes 78458f0484fSRodney W. Grimes if (p != NULL) { 78558f0484fSRodney W. Grimes size = p - cp; 78658f0484fSRodney W. Grimes if (size > prec) 78758f0484fSRodney W. Grimes size = prec; 78858f0484fSRodney W. Grimes } else 78958f0484fSRodney W. Grimes size = prec; 79058f0484fSRodney W. Grimes } else 79158f0484fSRodney W. Grimes size = strlen(cp); 79258f0484fSRodney W. Grimes sign = '\0'; 79358f0484fSRodney W. Grimes break; 79458f0484fSRodney W. Grimes case 'U': 79558f0484fSRodney W. Grimes flags |= LONGINT; 79658f0484fSRodney W. Grimes /*FALLTHROUGH*/ 79758f0484fSRodney W. Grimes case 'u': 7987735bb0fSBill Fenner if (flags & INTMAX_SIZE) 7997735bb0fSBill Fenner ujval = UJARG(); 80058f0484fSRodney W. Grimes else 80158f0484fSRodney W. Grimes ulval = UARG(); 80258f0484fSRodney W. Grimes base = 10; 80358f0484fSRodney W. Grimes goto nosign; 80458f0484fSRodney W. Grimes case 'X': 805ebbad5ecSDavid Schultz xdigs = xdigs_upper; 80658f0484fSRodney W. Grimes goto hex; 80758f0484fSRodney W. Grimes case 'x': 808ebbad5ecSDavid Schultz xdigs = xdigs_lower; 8097735bb0fSBill Fenner hex: 8107735bb0fSBill Fenner if (flags & INTMAX_SIZE) 8117735bb0fSBill Fenner ujval = UJARG(); 81258f0484fSRodney W. Grimes else 81358f0484fSRodney W. Grimes ulval = UARG(); 81458f0484fSRodney W. Grimes base = 16; 81558f0484fSRodney W. Grimes /* leading 0x/X only if non-zero */ 81658f0484fSRodney W. Grimes if (flags & ALT && 8177735bb0fSBill Fenner (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) 818ebbad5ecSDavid Schultz ox[1] = ch; 81958f0484fSRodney W. Grimes 82098ee7635SAlexey Zelkin flags &= ~GROUPING; 82158f0484fSRodney W. Grimes /* unsigned conversions */ 82258f0484fSRodney W. Grimes nosign: sign = '\0'; 8232e394b2fSAlexey Zelkin /*- 82458f0484fSRodney W. Grimes * ``... diouXx conversions ... if a precision is 82558f0484fSRodney W. Grimes * specified, the 0 flag will be ignored.'' 82658f0484fSRodney W. Grimes * -- ANSI X3J11 82758f0484fSRodney W. Grimes */ 82858f0484fSRodney W. Grimes number: if ((dprec = prec) >= 0) 82958f0484fSRodney W. Grimes flags &= ~ZEROPAD; 83058f0484fSRodney W. Grimes 8312e394b2fSAlexey Zelkin /*- 83258f0484fSRodney W. Grimes * ``The result of converting a zero value with an 83358f0484fSRodney W. Grimes * explicit precision of zero is no characters.'' 83458f0484fSRodney W. Grimes * -- ANSI X3J11 8351be5319aSDavid Schultz * 8361be5319aSDavid Schultz * ``The C Standard is clear enough as is. The call 8371be5319aSDavid Schultz * printf("%#.0o", 0) should print 0.'' 8381be5319aSDavid Schultz * -- Defect Report #151 83958f0484fSRodney W. Grimes */ 84058f0484fSRodney W. Grimes cp = buf + BUF; 8417735bb0fSBill Fenner if (flags & INTMAX_SIZE) { 8421be5319aSDavid Schultz if (ujval != 0 || prec != 0 || 8431be5319aSDavid Schultz (flags & ALT && base == 8)) 8447735bb0fSBill Fenner cp = __ujtoa(ujval, cp, base, 84598ee7635SAlexey Zelkin flags & ALT, xdigs, 84698ee7635SAlexey Zelkin flags & GROUPING, thousands_sep, 84798ee7635SAlexey Zelkin grouping); 84858f0484fSRodney W. Grimes } else { 8491be5319aSDavid Schultz if (ulval != 0 || prec != 0 || 8501be5319aSDavid Schultz (flags & ALT && base == 8)) 85158f0484fSRodney W. Grimes cp = __ultoa(ulval, cp, base, 85298ee7635SAlexey Zelkin flags & ALT, xdigs, 85398ee7635SAlexey Zelkin flags & GROUPING, thousands_sep, 85498ee7635SAlexey Zelkin grouping); 85558f0484fSRodney W. Grimes } 85658f0484fSRodney W. Grimes size = buf + BUF - cp; 85738cac8f8SDavid Schultz if (size > BUF) /* should never happen */ 85838cac8f8SDavid Schultz abort(); 85958f0484fSRodney W. Grimes break; 86058f0484fSRodney W. Grimes default: /* "%?" prints ?, unless ? is NUL */ 86158f0484fSRodney W. Grimes if (ch == '\0') 86258f0484fSRodney W. Grimes goto done; 86358f0484fSRodney W. Grimes /* pretend it was %c with argument ch */ 86458f0484fSRodney W. Grimes cp = buf; 86558f0484fSRodney W. Grimes *cp = ch; 86658f0484fSRodney W. Grimes size = 1; 86758f0484fSRodney W. Grimes sign = '\0'; 86858f0484fSRodney W. Grimes break; 86958f0484fSRodney W. Grimes } 87058f0484fSRodney W. Grimes 87158f0484fSRodney W. Grimes /* 87258f0484fSRodney W. Grimes * All reasonable formats wind up here. At this point, `cp' 87358f0484fSRodney W. Grimes * points to a string which (if not flags&LADJUST) should be 87458f0484fSRodney W. Grimes * padded out to `width' places. If flags&ZEROPAD, it should 87558f0484fSRodney W. Grimes * first be prefixed by any sign or other prefix; otherwise, 87658f0484fSRodney W. Grimes * it should be blank padded before the prefix is emitted. 87758f0484fSRodney W. Grimes * After any left-hand padding and prefixing, emit zeroes 87858f0484fSRodney W. Grimes * required by a decimal [diouxX] precision, then print the 87958f0484fSRodney W. Grimes * string proper, then emit zeroes required by any leftover 88058f0484fSRodney W. Grimes * floating precision; finally, if LADJUST, pad with blanks. 88158f0484fSRodney W. Grimes * 88258f0484fSRodney W. Grimes * Compute actual size, so we know how much to pad. 883261a532aSBill Fenner * size excludes decimal prec; realsz includes it. 88458f0484fSRodney W. Grimes */ 885261a532aSBill Fenner realsz = dprec > size ? dprec : size; 88658f0484fSRodney W. Grimes if (sign) 887261a532aSBill Fenner realsz++; 888904322a5SDavid Schultz if (ox[1]) 889261a532aSBill Fenner realsz += 2; 89058f0484fSRodney W. Grimes 89192e88f87SAndrey A. Chernov prsize = width > realsz ? width : realsz; 892b250f248SAndrey A. Chernov if ((unsigned)ret + prsize > INT_MAX) { 89392e88f87SAndrey A. Chernov ret = EOF; 89492e88f87SAndrey A. Chernov goto error; 89592e88f87SAndrey A. Chernov } 89692e88f87SAndrey A. Chernov 89758f0484fSRodney W. Grimes /* right-adjusting blank padding */ 89858f0484fSRodney W. Grimes if ((flags & (LADJUST|ZEROPAD)) == 0) 89958f0484fSRodney W. Grimes PAD(width - realsz, blanks); 90058f0484fSRodney W. Grimes 90158f0484fSRodney W. Grimes /* prefix */ 902904322a5SDavid Schultz if (sign) 90358f0484fSRodney W. Grimes PRINT(&sign, 1); 904904322a5SDavid Schultz 905904322a5SDavid Schultz if (ox[1]) { /* ox[1] is either x, X, or \0 */ 90658f0484fSRodney W. Grimes ox[0] = '0'; 90758f0484fSRodney W. Grimes PRINT(ox, 2); 90858f0484fSRodney W. Grimes } 90958f0484fSRodney W. Grimes 91058f0484fSRodney W. Grimes /* right-adjusting zero padding */ 91158f0484fSRodney W. Grimes if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 91258f0484fSRodney W. Grimes PAD(width - realsz, zeroes); 91358f0484fSRodney W. Grimes 91458f0484fSRodney W. Grimes /* leading zeroes from decimal precision */ 915261a532aSBill Fenner PAD(dprec - size, zeroes); 91658f0484fSRodney W. Grimes 91758f0484fSRodney W. Grimes /* the string or number proper */ 9188de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 91958f0484fSRodney W. Grimes if ((flags & FPT) == 0) { 92058f0484fSRodney W. Grimes PRINT(cp, size); 92158f0484fSRodney W. Grimes } else { /* glue together f_p fragments */ 922ebbad5ecSDavid Schultz if (!expchar) { /* %[fF] or sufficiently short %[gG] */ 923ebbad5ecSDavid Schultz if (expt <= 0) { 92481ae2e9aSDavid Schultz PRINT(zeroes, 1); 92581ae2e9aSDavid Schultz if (prec || flags & ALT) 9265004a238SDavid Schultz PRINT(decimal_point,decpt_len); 92758f0484fSRodney W. Grimes PAD(-expt, zeroes); 9283b204b7dSDavid Schultz /* already handled initial 0's */ 9293b204b7dSDavid Schultz prec += expt; 93058f0484fSRodney W. Grimes } else { 9313b204b7dSDavid Schultz PRINTANDPAD(cp, dtoaend, lead, zeroes); 932ebbad5ecSDavid Schultz cp += lead; 933ebbad5ecSDavid Schultz if (grouping) { 934ebbad5ecSDavid Schultz while (nseps>0 || nrepeats>0) { 935ebbad5ecSDavid Schultz if (nrepeats > 0) 936ebbad5ecSDavid Schultz nrepeats--; 937ebbad5ecSDavid Schultz else { 938ebbad5ecSDavid Schultz grouping--; 939ebbad5ecSDavid Schultz nseps--; 94058f0484fSRodney W. Grimes } 941ebbad5ecSDavid Schultz PRINT(&thousands_sep, 942ebbad5ecSDavid Schultz 1); 9433b204b7dSDavid Schultz PRINTANDPAD(cp,dtoaend, 9443b204b7dSDavid Schultz *grouping, zeroes); 945ebbad5ecSDavid Schultz cp += *grouping; 946ebbad5ecSDavid Schultz } 9473b204b7dSDavid Schultz if (cp > dtoaend) 9483b204b7dSDavid Schultz cp = dtoaend; 949ebbad5ecSDavid Schultz } 950ebbad5ecSDavid Schultz if (prec || flags & ALT) 9515004a238SDavid Schultz PRINT(decimal_point,decpt_len); 952ebbad5ecSDavid Schultz } 9533b204b7dSDavid Schultz PRINTANDPAD(cp, dtoaend, prec, zeroes); 954ebbad5ecSDavid Schultz } else { /* %[eE] or sufficiently long %[gG] */ 9553b204b7dSDavid Schultz if (prec > 1 || flags & ALT) { 9565004a238SDavid Schultz PRINT(cp++, 1); 9575004a238SDavid Schultz PRINT(decimal_point, decpt_len); 95858f0484fSRodney W. Grimes PRINT(cp, ndig-1); 959ebbad5ecSDavid Schultz PAD(prec - ndig, zeroes); 96058f0484fSRodney W. Grimes } else /* XeYYY */ 96158f0484fSRodney W. Grimes PRINT(cp, 1); 96258f0484fSRodney W. Grimes PRINT(expstr, expsize); 96358f0484fSRodney W. Grimes } 96458f0484fSRodney W. Grimes } 96558f0484fSRodney W. Grimes #else 96658f0484fSRodney W. Grimes PRINT(cp, size); 96758f0484fSRodney W. Grimes #endif 96858f0484fSRodney W. Grimes /* left-adjusting padding (always blank) */ 96958f0484fSRodney W. Grimes if (flags & LADJUST) 97058f0484fSRodney W. Grimes PAD(width - realsz, blanks); 97158f0484fSRodney W. Grimes 97258f0484fSRodney W. Grimes /* finally, adjust ret */ 97392e88f87SAndrey A. Chernov ret += prsize; 97458f0484fSRodney W. Grimes 97558f0484fSRodney W. Grimes FLUSH(); /* copy out the I/O vectors */ 97658f0484fSRodney W. Grimes } 97758f0484fSRodney W. Grimes done: 97858f0484fSRodney W. Grimes FLUSH(); 97958f0484fSRodney W. Grimes error: 980096ad104SDag-Erling Smørgrav va_end(orgap); 9818de9e897SDavid Schultz #ifndef NO_FLOATING_POINT 9822ffc61baSTor Egge if (dtoaresult != NULL) 983ebbad5ecSDavid Schultz freedtoa(dtoaresult); 9842ffc61baSTor Egge #endif 985b9aac308STim J. Robbins if (convbuf != NULL) 986b9aac308STim J. Robbins free(convbuf); 987f70177e7SJulian Elischer if (__sferror(fp)) 988f70177e7SJulian Elischer ret = EOF; 989efb7e53dSJordan K. Hubbard if ((argtable != NULL) && (argtable != statargtable)) 990efb7e53dSJordan K. Hubbard free (argtable); 991f70177e7SJulian Elischer return (ret); 99258f0484fSRodney W. Grimes /* NOTREACHED */ 99358f0484fSRodney W. Grimes } 99458f0484fSRodney W. Grimes 995