1c5604d0aSTim J. Robbins /*- 2c5604d0aSTim J. Robbins * Copyright (c) 1990, 1993 3c5604d0aSTim J. Robbins * The Regents of the University of California. All rights reserved. 4c5604d0aSTim J. Robbins * 5c5604d0aSTim J. Robbins * This code is derived from software contributed to Berkeley by 6c5604d0aSTim J. Robbins * Chris Torek. 7c5604d0aSTim J. Robbins * 8c5604d0aSTim J. Robbins * Redistribution and use in source and binary forms, with or without 9c5604d0aSTim J. Robbins * modification, are permitted provided that the following conditions 10c5604d0aSTim J. Robbins * are met: 11c5604d0aSTim J. Robbins * 1. Redistributions of source code must retain the above copyright 12c5604d0aSTim J. Robbins * notice, this list of conditions and the following disclaimer. 13c5604d0aSTim J. Robbins * 2. Redistributions in binary form must reproduce the above copyright 14c5604d0aSTim J. Robbins * notice, this list of conditions and the following disclaimer in the 15c5604d0aSTim J. Robbins * documentation and/or other materials provided with the distribution. 16c5604d0aSTim J. Robbins * 4. Neither the name of the University nor the names of its contributors 17c5604d0aSTim J. Robbins * may be used to endorse or promote products derived from this software 18c5604d0aSTim J. Robbins * without specific prior written permission. 19c5604d0aSTim J. Robbins * 20c5604d0aSTim J. Robbins * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21c5604d0aSTim J. Robbins * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22c5604d0aSTim J. Robbins * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23c5604d0aSTim J. Robbins * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24c5604d0aSTim J. Robbins * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25c5604d0aSTim J. Robbins * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26c5604d0aSTim J. Robbins * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27c5604d0aSTim J. Robbins * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28c5604d0aSTim J. Robbins * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29c5604d0aSTim J. Robbins * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30c5604d0aSTim J. Robbins * SUCH DAMAGE. 31c5604d0aSTim J. Robbins */ 32c5604d0aSTim J. Robbins 33c5604d0aSTim J. Robbins #if 0 34c5604d0aSTim J. Robbins #if defined(LIBC_SCCS) && !defined(lint) 35c5604d0aSTim J. Robbins static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; 36c5604d0aSTim J. Robbins #endif /* LIBC_SCCS and not lint */ 37c5604d0aSTim J. Robbins #endif 38af152640SDavid E. O'Brien #include <sys/cdefs.h> 39c5604d0aSTim J. Robbins __FBSDID("$FreeBSD$"); 40c5604d0aSTim J. Robbins 41c5604d0aSTim J. Robbins /* 42c5604d0aSTim J. Robbins * Actual wprintf innards. 43c5604d0aSTim J. Robbins * 44c5604d0aSTim J. Robbins * Avoid making gratuitous changes to this source file; it should be kept 45c5604d0aSTim J. Robbins * as close as possible to vfprintf.c for ease of maintenance. 46c5604d0aSTim J. Robbins */ 47c5604d0aSTim J. Robbins 48c5604d0aSTim J. Robbins #include "namespace.h" 49c5604d0aSTim J. Robbins #include <sys/types.h> 50c5604d0aSTim J. Robbins 51c5604d0aSTim J. Robbins #include <ctype.h> 52c5604d0aSTim J. Robbins #include <limits.h> 53c5604d0aSTim J. Robbins #include <locale.h> 54c5604d0aSTim J. Robbins #include <stdarg.h> 55c5604d0aSTim J. Robbins #include <stddef.h> 56c5604d0aSTim J. Robbins #include <stdint.h> 57c5604d0aSTim J. Robbins #include <stdio.h> 58c5604d0aSTim J. Robbins #include <stdlib.h> 59c5604d0aSTim J. Robbins #include <string.h> 60c5604d0aSTim J. Robbins #include <wchar.h> 61c5604d0aSTim J. Robbins #include <wctype.h> 62c5604d0aSTim J. Robbins #include "un-namespace.h" 63c5604d0aSTim J. Robbins 64c5604d0aSTim J. Robbins #include "libc_private.h" 65c5604d0aSTim J. Robbins #include "local.h" 66c5604d0aSTim J. Robbins #include "fvwrite.h" 672591efccSDavid Schultz #include "printflocal.h" 68e5abb5e6SDavid Schultz 69814d1bc9SDavid Schultz static int __sprint(FILE *, struct __suio *); 70a1805f7bSDavid Schultz static int __sbprintf(FILE *, const wchar_t *, va_list) __noinline; 71909a17f4STim J. Robbins static wint_t __xfputwc(wchar_t, FILE *); 72c5604d0aSTim J. Robbins static wchar_t *__mbsconv(char *, int); 73c5604d0aSTim J. Robbins 74814d1bc9SDavid Schultz #define CHAR wchar_t 75814d1bc9SDavid Schultz #include "printfcommon.h" 76814d1bc9SDavid Schultz 7721ca178eSDavid Schultz struct grouping_state { 7821ca178eSDavid Schultz wchar_t thousands_sep; /* locale-specific thousands separator */ 7921ca178eSDavid Schultz const char *grouping; /* locale-specific numeric grouping rules */ 8021ca178eSDavid Schultz int lead; /* sig figs before decimal or group sep */ 8121ca178eSDavid Schultz int nseps; /* number of group separators with ' */ 8221ca178eSDavid Schultz int nrepeats; /* number of repeats of the last group */ 8321ca178eSDavid Schultz }; 8421ca178eSDavid Schultz 855004a238SDavid Schultz static const mbstate_t initial_mbs; 865004a238SDavid Schultz 875004a238SDavid Schultz static inline wchar_t 885004a238SDavid Schultz get_decpt(void) 895004a238SDavid Schultz { 905004a238SDavid Schultz mbstate_t mbs; 915004a238SDavid Schultz wchar_t decpt; 925004a238SDavid Schultz int nconv; 935004a238SDavid Schultz 945004a238SDavid Schultz mbs = initial_mbs; 955004a238SDavid Schultz nconv = mbrtowc(&decpt, localeconv()->decimal_point, MB_CUR_MAX, &mbs); 965004a238SDavid Schultz if (nconv == (size_t)-1 || nconv == (size_t)-2) 975004a238SDavid Schultz decpt = '.'; /* failsafe */ 985004a238SDavid Schultz return (decpt); 995004a238SDavid Schultz } 1005004a238SDavid Schultz 10121ca178eSDavid Schultz static inline wchar_t 10221ca178eSDavid Schultz get_thousep(void) 10321ca178eSDavid Schultz { 10421ca178eSDavid Schultz mbstate_t mbs; 10521ca178eSDavid Schultz wchar_t thousep; 10621ca178eSDavid Schultz int nconv; 10721ca178eSDavid Schultz 10821ca178eSDavid Schultz mbs = initial_mbs; 10921ca178eSDavid Schultz nconv = mbrtowc(&thousep, localeconv()->thousands_sep, 11021ca178eSDavid Schultz MB_CUR_MAX, &mbs); 11121ca178eSDavid Schultz if (nconv == (size_t)-1 || nconv == (size_t)-2) 11221ca178eSDavid Schultz thousep = '\0'; /* failsafe */ 11321ca178eSDavid Schultz return (thousep); 11421ca178eSDavid Schultz } 11521ca178eSDavid Schultz 11621ca178eSDavid Schultz /* 11721ca178eSDavid Schultz * Initialize the thousands' grouping state in preparation to print a 11821ca178eSDavid Schultz * number with ndigits digits. This routine returns the total number 11921ca178eSDavid Schultz * of wide characters that will be printed. 12021ca178eSDavid Schultz */ 12121ca178eSDavid Schultz static int 12221ca178eSDavid Schultz grouping_init(struct grouping_state *gs, int ndigits) 12321ca178eSDavid Schultz { 12421ca178eSDavid Schultz 12521ca178eSDavid Schultz gs->grouping = localeconv()->grouping; 12621ca178eSDavid Schultz gs->thousands_sep = get_thousep(); 12721ca178eSDavid Schultz 12821ca178eSDavid Schultz gs->nseps = gs->nrepeats = 0; 12921ca178eSDavid Schultz gs->lead = ndigits; 13021ca178eSDavid Schultz while (*gs->grouping != CHAR_MAX) { 13121ca178eSDavid Schultz if (gs->lead <= *gs->grouping) 13221ca178eSDavid Schultz break; 13321ca178eSDavid Schultz gs->lead -= *gs->grouping; 13421ca178eSDavid Schultz if (*(gs->grouping+1)) { 13521ca178eSDavid Schultz gs->nseps++; 13621ca178eSDavid Schultz gs->grouping++; 13721ca178eSDavid Schultz } else 13821ca178eSDavid Schultz gs->nrepeats++; 13921ca178eSDavid Schultz } 14021ca178eSDavid Schultz return (gs->nseps + gs->nrepeats); 14121ca178eSDavid Schultz } 14221ca178eSDavid Schultz 14321ca178eSDavid Schultz /* 14421ca178eSDavid Schultz * Print a number with thousands' separators. 14521ca178eSDavid Schultz */ 14621ca178eSDavid Schultz static int 14721ca178eSDavid Schultz grouping_print(struct grouping_state *gs, struct io_state *iop, 14821ca178eSDavid Schultz const CHAR *cp, const CHAR *ep) 14921ca178eSDavid Schultz { 15021ca178eSDavid Schultz const CHAR *cp0 = cp; 15121ca178eSDavid Schultz 15221ca178eSDavid Schultz if (io_printandpad(iop, cp, ep, gs->lead, zeroes)) 15321ca178eSDavid Schultz return (-1); 15421ca178eSDavid Schultz cp += gs->lead; 15521ca178eSDavid Schultz while (gs->nseps > 0 || gs->nrepeats > 0) { 15621ca178eSDavid Schultz if (gs->nrepeats > 0) 15721ca178eSDavid Schultz gs->nrepeats--; 15821ca178eSDavid Schultz else { 15921ca178eSDavid Schultz gs->grouping--; 16021ca178eSDavid Schultz gs->nseps--; 16121ca178eSDavid Schultz } 16221ca178eSDavid Schultz if (io_print(iop, &gs->thousands_sep, 1)) 16321ca178eSDavid Schultz return (-1); 16421ca178eSDavid Schultz if (io_printandpad(iop, cp, ep, *gs->grouping, zeroes)) 16521ca178eSDavid Schultz return (-1); 16621ca178eSDavid Schultz cp += *gs->grouping; 16721ca178eSDavid Schultz } 16821ca178eSDavid Schultz if (cp > ep) 16921ca178eSDavid Schultz cp = ep; 17021ca178eSDavid Schultz return (cp - cp0); 17121ca178eSDavid Schultz } 17221ca178eSDavid Schultz 17321ca178eSDavid Schultz 174814d1bc9SDavid Schultz /* 175814d1bc9SDavid Schultz * Flush out all the vectors defined by the given uio, 176814d1bc9SDavid Schultz * then reset it so that it can be reused. 177814d1bc9SDavid Schultz * 178814d1bc9SDavid Schultz * XXX The fact that we do this a character at a time and convert to a 179814d1bc9SDavid Schultz * multibyte character sequence even if the destination is a wide 180814d1bc9SDavid Schultz * string eclipses the benefits of buffering. 181814d1bc9SDavid Schultz */ 182814d1bc9SDavid Schultz static int 183814d1bc9SDavid Schultz __sprint(FILE *fp, struct __suio *uio) 184814d1bc9SDavid Schultz { 185814d1bc9SDavid Schultz struct __siov *iov; 186814d1bc9SDavid Schultz wchar_t *p; 187814d1bc9SDavid Schultz int i, len; 188814d1bc9SDavid Schultz 189814d1bc9SDavid Schultz iov = uio->uio_iov; 190814d1bc9SDavid Schultz for (; uio->uio_resid != 0; uio->uio_resid -= len, iov++) { 191814d1bc9SDavid Schultz p = (wchar_t *)iov->iov_base; 192814d1bc9SDavid Schultz len = iov->iov_len; 193814d1bc9SDavid Schultz for (i = 0; i < len; i++) { 194814d1bc9SDavid Schultz if (__xfputwc(p[i], fp) == WEOF) 195814d1bc9SDavid Schultz return (-1); 196814d1bc9SDavid Schultz } 197814d1bc9SDavid Schultz } 198814d1bc9SDavid Schultz uio->uio_iovcnt = 0; 199814d1bc9SDavid Schultz return (0); 200814d1bc9SDavid Schultz } 201814d1bc9SDavid Schultz 202c5604d0aSTim J. Robbins /* 203c5604d0aSTim J. Robbins * Helper function for `fprintf to unbuffered unix file': creates a 204c5604d0aSTim J. Robbins * temporary buffer. We only work on write-only files; this avoids 205c5604d0aSTim J. Robbins * worries about ungetc buffers and so forth. 206c5604d0aSTim J. Robbins */ 207c5604d0aSTim J. Robbins static int 208c5604d0aSTim J. Robbins __sbprintf(FILE *fp, const wchar_t *fmt, va_list ap) 209c5604d0aSTim J. Robbins { 210c5604d0aSTim J. Robbins int ret; 211c5604d0aSTim J. Robbins FILE fake; 212c5604d0aSTim J. Robbins unsigned char buf[BUFSIZ]; 213c5604d0aSTim J. Robbins 214a1805f7bSDavid Schultz /* XXX This is probably not needed. */ 215a1805f7bSDavid Schultz if (prepwrite(fp) != 0) 216a1805f7bSDavid Schultz return (EOF); 217a1805f7bSDavid Schultz 218c5604d0aSTim J. Robbins /* copy the important variables */ 219c5604d0aSTim J. Robbins fake._flags = fp->_flags & ~__SNBF; 220c5604d0aSTim J. Robbins fake._file = fp->_file; 221c5604d0aSTim J. Robbins fake._cookie = fp->_cookie; 222c5604d0aSTim J. Robbins fake._write = fp->_write; 2231e98f887SJohn Baldwin fake._orientation = fp->_orientation; 2241e98f887SJohn Baldwin fake._mbstate = fp->_mbstate; 225c5604d0aSTim J. Robbins 226c5604d0aSTim J. Robbins /* set up the buffer */ 227c5604d0aSTim J. Robbins fake._bf._base = fake._p = buf; 228c5604d0aSTim J. Robbins fake._bf._size = fake._w = sizeof(buf); 229c5604d0aSTim J. Robbins fake._lbfsize = 0; /* not actually used, but Just In Case */ 230c5604d0aSTim J. Robbins 231c5604d0aSTim J. Robbins /* do the work, then copy any error status */ 232c5604d0aSTim J. Robbins ret = __vfwprintf(&fake, fmt, ap); 233c5604d0aSTim J. Robbins if (ret >= 0 && __fflush(&fake)) 234c5604d0aSTim J. Robbins ret = WEOF; 235c5604d0aSTim J. Robbins if (fake._flags & __SERR) 236c5604d0aSTim J. Robbins fp->_flags |= __SERR; 237c5604d0aSTim J. Robbins return (ret); 238c5604d0aSTim J. Robbins } 239c5604d0aSTim J. Robbins 240c5604d0aSTim J. Robbins /* 241909a17f4STim J. Robbins * Like __fputwc, but handles fake string (__SSTR) files properly. 242909a17f4STim J. Robbins * File must already be locked. 243909a17f4STim J. Robbins */ 244909a17f4STim J. Robbins static wint_t 245909a17f4STim J. Robbins __xfputwc(wchar_t wc, FILE *fp) 246909a17f4STim J. Robbins { 24793996f6dSTim J. Robbins mbstate_t mbs; 248909a17f4STim J. Robbins char buf[MB_LEN_MAX]; 249909a17f4STim J. Robbins struct __suio uio; 250909a17f4STim J. Robbins struct __siov iov; 25184d9142fSJacques Vidrine size_t len; 252909a17f4STim J. Robbins 253909a17f4STim J. Robbins if ((fp->_flags & __SSTR) == 0) 254909a17f4STim J. Robbins return (__fputwc(wc, fp)); 255909a17f4STim J. Robbins 2565004a238SDavid Schultz mbs = initial_mbs; 25793996f6dSTim J. Robbins if ((len = wcrtomb(buf, wc, &mbs)) == (size_t)-1) { 258909a17f4STim J. Robbins fp->_flags |= __SERR; 259909a17f4STim J. Robbins return (WEOF); 260909a17f4STim J. Robbins } 261909a17f4STim J. Robbins uio.uio_iov = &iov; 262909a17f4STim J. Robbins uio.uio_resid = len; 263909a17f4STim J. Robbins uio.uio_iovcnt = 1; 264909a17f4STim J. Robbins iov.iov_base = buf; 265909a17f4STim J. Robbins iov.iov_len = len; 266909a17f4STim J. Robbins return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF); 267909a17f4STim J. Robbins } 268909a17f4STim J. Robbins 269909a17f4STim J. Robbins /* 270c5604d0aSTim J. Robbins * Convert a multibyte character string argument for the %s format to a wide 271c5604d0aSTim J. Robbins * string representation. ``prec'' specifies the maximum number of bytes 272c5604d0aSTim J. Robbins * to output. If ``prec'' is greater than or equal to zero, we can't assume 273c5604d0aSTim J. Robbins * that the multibyte char. string ends in a null character. 274c5604d0aSTim J. Robbins */ 275c5604d0aSTim J. Robbins static wchar_t * 276c5604d0aSTim J. Robbins __mbsconv(char *mbsarg, int prec) 277c5604d0aSTim J. Robbins { 27893996f6dSTim J. Robbins mbstate_t mbs; 279c5604d0aSTim J. Robbins wchar_t *convbuf, *wcp; 280c5604d0aSTim J. Robbins const char *p; 281c5604d0aSTim J. Robbins size_t insize, nchars, nconv; 282c5604d0aSTim J. Robbins 283adfd6b31STim J. Robbins if (mbsarg == NULL) 284adfd6b31STim J. Robbins return (NULL); 285adfd6b31STim J. Robbins 286c5604d0aSTim J. Robbins /* 287c5604d0aSTim J. Robbins * Supplied argument is a multibyte string; convert it to wide 288c5604d0aSTim J. Robbins * characters first. 289c5604d0aSTim J. Robbins */ 290c5604d0aSTim J. Robbins if (prec >= 0) { 291c5604d0aSTim J. Robbins /* 292c5604d0aSTim J. Robbins * String is not guaranteed to be NUL-terminated. Find the 293c5604d0aSTim J. Robbins * number of characters to print. 294c5604d0aSTim J. Robbins */ 295c5604d0aSTim J. Robbins p = mbsarg; 296c5604d0aSTim J. Robbins insize = nchars = 0; 2975004a238SDavid Schultz mbs = initial_mbs; 298c5604d0aSTim J. Robbins while (nchars != (size_t)prec) { 29993996f6dSTim J. Robbins nconv = mbrlen(p, MB_CUR_MAX, &mbs); 300c5604d0aSTim J. Robbins if (nconv == 0 || nconv == (size_t)-1 || 301c5604d0aSTim J. Robbins nconv == (size_t)-2) 302c5604d0aSTim J. Robbins break; 303c5604d0aSTim J. Robbins p += nconv; 304c5604d0aSTim J. Robbins nchars++; 305c5604d0aSTim J. Robbins insize += nconv; 306c5604d0aSTim J. Robbins } 307c5604d0aSTim J. Robbins if (nconv == (size_t)-1 || nconv == (size_t)-2) 308c5604d0aSTim J. Robbins return (NULL); 30922d725b5SColin Percival } else { 310c5604d0aSTim J. Robbins insize = strlen(mbsarg); 31122d725b5SColin Percival nconv = 0; 31222d725b5SColin Percival } 313c5604d0aSTim J. Robbins 314c5604d0aSTim J. Robbins /* 315c5604d0aSTim J. Robbins * Allocate buffer for the result and perform the conversion, 316c5604d0aSTim J. Robbins * converting at most `size' bytes of the input multibyte string to 317c5604d0aSTim J. Robbins * wide characters for printing. 318c5604d0aSTim J. Robbins */ 319c5604d0aSTim J. Robbins convbuf = malloc((insize + 1) * sizeof(*convbuf)); 320c5604d0aSTim J. Robbins if (convbuf == NULL) 321c5604d0aSTim J. Robbins return (NULL); 322c5604d0aSTim J. Robbins wcp = convbuf; 323c5604d0aSTim J. Robbins p = mbsarg; 3245004a238SDavid Schultz mbs = initial_mbs; 325c5604d0aSTim J. Robbins while (insize != 0) { 32693996f6dSTim J. Robbins nconv = mbrtowc(wcp, p, insize, &mbs); 327c5604d0aSTim J. Robbins if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) 328c5604d0aSTim J. Robbins break; 329c5604d0aSTim J. Robbins wcp++; 330c5604d0aSTim J. Robbins p += nconv; 331c5604d0aSTim J. Robbins insize -= nconv; 332c5604d0aSTim J. Robbins } 333c5604d0aSTim J. Robbins if (nconv == (size_t)-1 || nconv == (size_t)-2) { 334c5604d0aSTim J. Robbins free(convbuf); 335c5604d0aSTim J. Robbins return (NULL); 336c5604d0aSTim J. Robbins } 337c5604d0aSTim J. Robbins *wcp = L'\0'; 338c5604d0aSTim J. Robbins 339c5604d0aSTim J. Robbins return (convbuf); 340c5604d0aSTim J. Robbins } 341c5604d0aSTim J. Robbins 342c5604d0aSTim J. Robbins /* 343c5604d0aSTim J. Robbins * MT-safe version 344c5604d0aSTim J. Robbins */ 345c5604d0aSTim J. Robbins int 346c5604d0aSTim J. Robbins vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, va_list ap) 347c5604d0aSTim J. Robbins 348c5604d0aSTim J. Robbins { 349c5604d0aSTim J. Robbins int ret; 350c5604d0aSTim J. Robbins 351c5604d0aSTim J. Robbins FLOCKFILE(fp); 352a1805f7bSDavid Schultz /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 353a1805f7bSDavid Schultz if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 354a1805f7bSDavid Schultz fp->_file >= 0) 355a1805f7bSDavid Schultz ret = __sbprintf(fp, fmt0, ap); 356a1805f7bSDavid Schultz else 357c5604d0aSTim J. Robbins ret = __vfwprintf(fp, fmt0, ap); 358c5604d0aSTim J. Robbins FUNLOCKFILE(fp); 359c5604d0aSTim J. Robbins return (ret); 360c5604d0aSTim J. Robbins } 361c5604d0aSTim J. Robbins 36238cac8f8SDavid Schultz /* 36338cac8f8SDavid Schultz * The size of the buffer we use as scratch space for integer 36421ca178eSDavid Schultz * conversions, among other things. We need enough space to 36521ca178eSDavid Schultz * write a uintmax_t in octal (plus one byte). 36638cac8f8SDavid Schultz */ 36721ca178eSDavid Schultz #if UINTMAX_MAX <= UINT64_MAX 36821ca178eSDavid Schultz #define BUF 32 36921ca178eSDavid Schultz #else 37021ca178eSDavid Schultz #error "BUF must be large enough to format a uintmax_t" 37121ca178eSDavid Schultz #endif 37238cac8f8SDavid Schultz 373c5604d0aSTim J. Robbins /* 374c5604d0aSTim J. Robbins * Non-MT-safe version 375c5604d0aSTim J. Robbins */ 376c5604d0aSTim J. Robbins int 377c5604d0aSTim J. Robbins __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) 378c5604d0aSTim J. Robbins { 379c5604d0aSTim J. Robbins wchar_t *fmt; /* format string */ 380c5604d0aSTim J. Robbins wchar_t ch; /* character from fmt */ 381814d1bc9SDavid Schultz int n, n2; /* handy integer (short term usage) */ 382c5604d0aSTim J. Robbins wchar_t *cp; /* handy char pointer (short term usage) */ 383c5604d0aSTim J. Robbins int flags; /* flags as above */ 384c5604d0aSTim J. Robbins int ret; /* return value accumulator */ 385c5604d0aSTim J. Robbins int width; /* width from format (%8d), or 0 */ 386adfd6b31STim J. Robbins int prec; /* precision from format; <0 for N/A */ 387c5604d0aSTim J. Robbins wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */ 38821ca178eSDavid Schultz struct grouping_state gs; /* thousands' grouping info */ 389ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT 390adfd6b31STim J. Robbins /* 391adfd6b31STim J. Robbins * We can decompose the printed representation of floating 392adfd6b31STim J. Robbins * point numbers into several parts, some of which may be empty: 393adfd6b31STim J. Robbins * 394adfd6b31STim J. Robbins * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ 395adfd6b31STim J. Robbins * A B ---C--- D E F 396adfd6b31STim J. Robbins * 397adfd6b31STim J. Robbins * A: 'sign' holds this value if present; '\0' otherwise 398adfd6b31STim J. Robbins * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal 399adfd6b31STim J. Robbins * C: cp points to the string MMMNNN. Leading and trailing 400adfd6b31STim J. Robbins * zeros are not in the string and must be added. 401adfd6b31STim J. Robbins * D: expchar holds this character; '\0' if no exponent, e.g. %f 402adfd6b31STim J. Robbins * F: at least two digits for decimal, at least one digit for hex 403adfd6b31STim J. Robbins */ 4045004a238SDavid Schultz wchar_t decimal_point; /* locale specific decimal point */ 405adfd6b31STim J. Robbins int signflag; /* true if float is negative */ 406adfd6b31STim J. Robbins union { /* floating point arguments %[aAeEfFgG] */ 407adfd6b31STim J. Robbins double dbl; 408adfd6b31STim J. Robbins long double ldbl; 409adfd6b31STim J. Robbins } fparg; 410c5604d0aSTim J. Robbins int expt; /* integer value of exponent */ 411adfd6b31STim J. Robbins char expchar; /* exponent character: [eEpP\0] */ 412adfd6b31STim J. Robbins char *dtoaend; /* pointer to end of converted digits */ 413c5604d0aSTim J. Robbins int expsize; /* character count for expstr */ 414adfd6b31STim J. Robbins int ndig; /* actual number of digits returned by dtoa */ 415adfd6b31STim J. Robbins wchar_t expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ 416adfd6b31STim J. Robbins char *dtoaresult; /* buffer allocated by dtoa */ 417c5604d0aSTim J. Robbins #endif 418c5604d0aSTim J. Robbins u_long ulval; /* integer arguments %[diouxX] */ 419c5604d0aSTim J. Robbins uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ 420c5604d0aSTim J. Robbins int base; /* base for [diouxX] conversion */ 421c5604d0aSTim J. Robbins int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 422c5604d0aSTim J. Robbins int realsz; /* field size expanded by dprec, sign, etc */ 423c5604d0aSTim J. Robbins int size; /* size of converted field or string */ 424c5604d0aSTim J. Robbins int prsize; /* max size of printed field */ 4257b7e3509SDavid Schultz const char *xdigs; /* digits for [xX] conversion */ 426814d1bc9SDavid Schultz struct io_state io; /* I/O buffering state */ 42738cac8f8SDavid Schultz wchar_t buf[BUF]; /* buffer with space for digits of uintmax_t */ 428c5604d0aSTim J. Robbins wchar_t ox[2]; /* space for 0x hex-prefix */ 429c5604d0aSTim J. Robbins union arg *argtable; /* args, built due to positional arg */ 430c5604d0aSTim J. Robbins union arg statargtable [STATIC_ARG_TBL_SIZE]; 431c5604d0aSTim J. Robbins int nextarg; /* 1-based argument index */ 432c5604d0aSTim J. Robbins va_list orgap; /* original argument pointer */ 433c5604d0aSTim J. Robbins wchar_t *convbuf; /* multibyte to wide conversion result */ 434c5604d0aSTim J. Robbins 4357b7e3509SDavid Schultz static const char xdigs_lower[16] = "0123456789abcdef"; 4367b7e3509SDavid Schultz static const char xdigs_upper[16] = "0123456789ABCDEF"; 437adfd6b31STim J. Robbins 438814d1bc9SDavid Schultz /* BEWARE, these `goto error' on error. */ 439c5604d0aSTim J. Robbins #define PRINT(ptr, len) do { \ 440814d1bc9SDavid Schultz if (io_print(&io, (ptr), (len))) \ 441814d1bc9SDavid Schultz goto error; \ 442c5604d0aSTim J. Robbins } while (0) 443814d1bc9SDavid Schultz #define PAD(howmany, with) { \ 444814d1bc9SDavid Schultz if (io_pad(&io, (howmany), (with))) \ 445814d1bc9SDavid Schultz goto error; \ 446814d1bc9SDavid Schultz } 447814d1bc9SDavid Schultz #define PRINTANDPAD(p, ep, len, with) { \ 448814d1bc9SDavid Schultz if (io_printandpad(&io, (p), (ep), (len), (with))) \ 449814d1bc9SDavid Schultz goto error; \ 450814d1bc9SDavid Schultz } 451814d1bc9SDavid Schultz #define FLUSH() { \ 452814d1bc9SDavid Schultz if (io_flush(&io)) \ 453814d1bc9SDavid Schultz goto error; \ 454814d1bc9SDavid Schultz } 455c5604d0aSTim J. Robbins 456c5604d0aSTim J. Robbins /* 457c5604d0aSTim J. Robbins * Get the argument indexed by nextarg. If the argument table is 458c5604d0aSTim J. Robbins * built, use it to get the argument. If its not, get the next 459c5604d0aSTim J. Robbins * argument (and arguments must be gotten sequentially). 460c5604d0aSTim J. Robbins */ 461c5604d0aSTim J. Robbins #define GETARG(type) \ 462c5604d0aSTim J. Robbins ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ 463c5604d0aSTim J. Robbins (nextarg++, va_arg(ap, type))) 464c5604d0aSTim J. Robbins 465c5604d0aSTim J. Robbins /* 466c5604d0aSTim J. Robbins * To extend shorts properly, we need both signed and unsigned 467c5604d0aSTim J. Robbins * argument extraction methods. 468c5604d0aSTim J. Robbins */ 469c5604d0aSTim J. Robbins #define SARG() \ 470c5604d0aSTim J. Robbins (flags&LONGINT ? GETARG(long) : \ 471c5604d0aSTim J. Robbins flags&SHORTINT ? (long)(short)GETARG(int) : \ 472c5604d0aSTim J. Robbins flags&CHARINT ? (long)(signed char)GETARG(int) : \ 473c5604d0aSTim J. Robbins (long)GETARG(int)) 474c5604d0aSTim J. Robbins #define UARG() \ 475c5604d0aSTim J. Robbins (flags&LONGINT ? GETARG(u_long) : \ 476c5604d0aSTim J. Robbins flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 477c5604d0aSTim J. Robbins flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ 478c5604d0aSTim J. Robbins (u_long)GETARG(u_int)) 479c5604d0aSTim J. Robbins #define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) 480c5604d0aSTim J. Robbins #define SJARG() \ 481c5604d0aSTim J. Robbins (flags&INTMAXT ? GETARG(intmax_t) : \ 4820881683bSDavid Schultz flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ 483c5604d0aSTim J. Robbins flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ 484c5604d0aSTim J. Robbins (intmax_t)GETARG(long long)) 485c5604d0aSTim J. Robbins #define UJARG() \ 486c5604d0aSTim J. Robbins (flags&INTMAXT ? GETARG(uintmax_t) : \ 487c5604d0aSTim J. Robbins flags&SIZET ? (uintmax_t)GETARG(size_t) : \ 488c5604d0aSTim J. Robbins flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ 489c5604d0aSTim J. Robbins (uintmax_t)GETARG(unsigned long long)) 490c5604d0aSTim J. Robbins 491c5604d0aSTim J. Robbins /* 492c5604d0aSTim J. Robbins * Get * arguments, including the form *nn$. Preserve the nextarg 493c5604d0aSTim J. Robbins * that the argument can be gotten once the type is determined. 494c5604d0aSTim J. Robbins */ 495c5604d0aSTim J. Robbins #define GETASTER(val) \ 496c5604d0aSTim J. Robbins n2 = 0; \ 497c5604d0aSTim J. Robbins cp = fmt; \ 498c5604d0aSTim J. Robbins while (is_digit(*cp)) { \ 499c5604d0aSTim J. Robbins n2 = 10 * n2 + to_digit(*cp); \ 500c5604d0aSTim J. Robbins cp++; \ 501c5604d0aSTim J. Robbins } \ 502c5604d0aSTim J. Robbins if (*cp == '$') { \ 503c5604d0aSTim J. Robbins int hold = nextarg; \ 504c5604d0aSTim J. Robbins if (argtable == NULL) { \ 505c5604d0aSTim J. Robbins argtable = statargtable; \ 506e62e5ff9SDavid Schultz if (__find_warguments (fmt0, orgap, &argtable)) { \ 507e62e5ff9SDavid Schultz ret = EOF; \ 508e62e5ff9SDavid Schultz goto error; \ 509e62e5ff9SDavid Schultz } \ 510c5604d0aSTim J. Robbins } \ 511c5604d0aSTim J. Robbins nextarg = n2; \ 512c5604d0aSTim J. Robbins val = GETARG (int); \ 513c5604d0aSTim J. Robbins nextarg = hold; \ 514c5604d0aSTim J. Robbins fmt = ++cp; \ 515c5604d0aSTim J. Robbins } else { \ 516c5604d0aSTim J. Robbins val = GETARG (int); \ 517c5604d0aSTim J. Robbins } 518c5604d0aSTim J. Robbins 519c5604d0aSTim J. Robbins 520c5604d0aSTim J. Robbins /* sorry, fwprintf(read_only_file, L"") returns WEOF, not 0 */ 52152183d46SDavid Schultz if (prepwrite(fp) != 0) 522c5604d0aSTim J. Robbins return (EOF); 523c5604d0aSTim J. Robbins 524e18701f4SDavid Schultz convbuf = NULL; 525c5604d0aSTim J. Robbins fmt = (wchar_t *)fmt0; 526c5604d0aSTim J. Robbins argtable = NULL; 527c5604d0aSTim J. Robbins nextarg = 1; 528d07090a8STim J. Robbins va_copy(orgap, ap); 529814d1bc9SDavid Schultz io_init(&io, fp); 530c5604d0aSTim J. Robbins ret = 0; 531e18701f4SDavid Schultz #ifndef NO_FLOATING_POINT 5325004a238SDavid Schultz decimal_point = get_decpt(); 533e18701f4SDavid Schultz #endif 534c5604d0aSTim J. Robbins 535c5604d0aSTim J. Robbins /* 536c5604d0aSTim J. Robbins * Scan the format for conversions (`%' character). 537c5604d0aSTim J. Robbins */ 538c5604d0aSTim J. Robbins for (;;) { 539c5604d0aSTim J. Robbins for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 540c5604d0aSTim J. Robbins /* void */; 541c5604d0aSTim J. Robbins if ((n = fmt - cp) != 0) { 542c5604d0aSTim J. Robbins if ((unsigned)ret + n > INT_MAX) { 543c5604d0aSTim J. Robbins ret = EOF; 544c5604d0aSTim J. Robbins goto error; 545c5604d0aSTim J. Robbins } 546c5604d0aSTim J. Robbins PRINT(cp, n); 547c5604d0aSTim J. Robbins ret += n; 548c5604d0aSTim J. Robbins } 549c5604d0aSTim J. Robbins if (ch == '\0') 550c5604d0aSTim J. Robbins goto done; 551c5604d0aSTim J. Robbins fmt++; /* skip over '%' */ 552c5604d0aSTim J. Robbins 553c5604d0aSTim J. Robbins flags = 0; 554c5604d0aSTim J. Robbins dprec = 0; 555c5604d0aSTim J. Robbins width = 0; 556c5604d0aSTim J. Robbins prec = -1; 55721ca178eSDavid Schultz gs.grouping = NULL; 558c5604d0aSTim J. Robbins sign = '\0'; 559adfd6b31STim J. Robbins ox[1] = '\0'; 560c5604d0aSTim J. Robbins 561c5604d0aSTim J. Robbins rflag: ch = *fmt++; 562c5604d0aSTim J. Robbins reswitch: switch (ch) { 563c5604d0aSTim J. Robbins case ' ': 564c5604d0aSTim J. Robbins /*- 565c5604d0aSTim J. Robbins * ``If the space and + flags both appear, the space 566c5604d0aSTim J. Robbins * flag will be ignored.'' 567c5604d0aSTim J. Robbins * -- ANSI X3J11 568c5604d0aSTim J. Robbins */ 569c5604d0aSTim J. Robbins if (!sign) 570c5604d0aSTim J. Robbins sign = ' '; 571c5604d0aSTim J. Robbins goto rflag; 572c5604d0aSTim J. Robbins case '#': 573c5604d0aSTim J. Robbins flags |= ALT; 574c5604d0aSTim J. Robbins goto rflag; 575c5604d0aSTim J. Robbins case '*': 576c5604d0aSTim J. Robbins /*- 577c5604d0aSTim J. Robbins * ``A negative field width argument is taken as a 578c5604d0aSTim J. Robbins * - flag followed by a positive field width.'' 579c5604d0aSTim J. Robbins * -- ANSI X3J11 580c5604d0aSTim J. Robbins * They don't exclude field widths read from args. 581c5604d0aSTim J. Robbins */ 582c5604d0aSTim J. Robbins GETASTER (width); 583c5604d0aSTim J. Robbins if (width >= 0) 584c5604d0aSTim J. Robbins goto rflag; 585c5604d0aSTim J. Robbins width = -width; 586c5604d0aSTim J. Robbins /* FALLTHROUGH */ 587c5604d0aSTim J. Robbins case '-': 588c5604d0aSTim J. Robbins flags |= LADJUST; 589c5604d0aSTim J. Robbins goto rflag; 590c5604d0aSTim J. Robbins case '+': 591c5604d0aSTim J. Robbins sign = '+'; 592c5604d0aSTim J. Robbins goto rflag; 593c5604d0aSTim J. Robbins case '\'': 594c5604d0aSTim J. Robbins flags |= GROUPING; 595c5604d0aSTim J. Robbins goto rflag; 596c5604d0aSTim J. Robbins case '.': 597c5604d0aSTim J. Robbins if ((ch = *fmt++) == '*') { 598adfd6b31STim J. Robbins GETASTER (prec); 599c5604d0aSTim J. Robbins goto rflag; 600c5604d0aSTim J. Robbins } 601adfd6b31STim J. Robbins prec = 0; 602c5604d0aSTim J. Robbins while (is_digit(ch)) { 603adfd6b31STim J. Robbins prec = 10 * prec + to_digit(ch); 604c5604d0aSTim J. Robbins ch = *fmt++; 605c5604d0aSTim J. Robbins } 606c5604d0aSTim J. Robbins goto reswitch; 607c5604d0aSTim J. Robbins case '0': 608c5604d0aSTim J. Robbins /*- 609c5604d0aSTim J. Robbins * ``Note that 0 is taken as a flag, not as the 610c5604d0aSTim J. Robbins * beginning of a field width.'' 611c5604d0aSTim J. Robbins * -- ANSI X3J11 612c5604d0aSTim J. Robbins */ 613c5604d0aSTim J. Robbins flags |= ZEROPAD; 614c5604d0aSTim J. Robbins goto rflag; 615c5604d0aSTim J. Robbins case '1': case '2': case '3': case '4': 616c5604d0aSTim J. Robbins case '5': case '6': case '7': case '8': case '9': 617c5604d0aSTim J. Robbins n = 0; 618c5604d0aSTim J. Robbins do { 619c5604d0aSTim J. Robbins n = 10 * n + to_digit(ch); 620c5604d0aSTim J. Robbins ch = *fmt++; 621c5604d0aSTim J. Robbins } while (is_digit(ch)); 622c5604d0aSTim J. Robbins if (ch == '$') { 623c5604d0aSTim J. Robbins nextarg = n; 624c5604d0aSTim J. Robbins if (argtable == NULL) { 625c5604d0aSTim J. Robbins argtable = statargtable; 626e62e5ff9SDavid Schultz if (__find_warguments (fmt0, orgap, 627e62e5ff9SDavid Schultz &argtable)) { 628e62e5ff9SDavid Schultz ret = EOF; 629e62e5ff9SDavid Schultz goto error; 630e62e5ff9SDavid Schultz } 631c5604d0aSTim J. Robbins } 632c5604d0aSTim J. Robbins goto rflag; 633c5604d0aSTim J. Robbins } 634c5604d0aSTim J. Robbins width = n; 635c5604d0aSTim J. Robbins goto reswitch; 636ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT 637c5604d0aSTim J. Robbins case 'L': 638c5604d0aSTim J. Robbins flags |= LONGDBL; 639c5604d0aSTim J. Robbins goto rflag; 640c5604d0aSTim J. Robbins #endif 641c5604d0aSTim J. Robbins case 'h': 642c5604d0aSTim J. Robbins if (flags & SHORTINT) { 643c5604d0aSTim J. Robbins flags &= ~SHORTINT; 644c5604d0aSTim J. Robbins flags |= CHARINT; 645c5604d0aSTim J. Robbins } else 646c5604d0aSTim J. Robbins flags |= SHORTINT; 647c5604d0aSTim J. Robbins goto rflag; 648c5604d0aSTim J. Robbins case 'j': 649c5604d0aSTim J. Robbins flags |= INTMAXT; 650c5604d0aSTim J. Robbins goto rflag; 651c5604d0aSTim J. Robbins case 'l': 652c5604d0aSTim J. Robbins if (flags & LONGINT) { 653c5604d0aSTim J. Robbins flags &= ~LONGINT; 654c5604d0aSTim J. Robbins flags |= LLONGINT; 655c5604d0aSTim J. Robbins } else 656c5604d0aSTim J. Robbins flags |= LONGINT; 657c5604d0aSTim J. Robbins goto rflag; 658c5604d0aSTim J. Robbins case 'q': 659c5604d0aSTim J. Robbins flags |= LLONGINT; /* not necessarily */ 660c5604d0aSTim J. Robbins goto rflag; 661c5604d0aSTim J. Robbins case 't': 662c5604d0aSTim J. Robbins flags |= PTRDIFFT; 663c5604d0aSTim J. Robbins goto rflag; 664c5604d0aSTim J. Robbins case 'z': 665c5604d0aSTim J. Robbins flags |= SIZET; 666c5604d0aSTim J. Robbins goto rflag; 667927ecbf3STim J. Robbins case 'C': 668927ecbf3STim J. Robbins flags |= LONGINT; 669927ecbf3STim J. Robbins /*FALLTHROUGH*/ 670c5604d0aSTim J. Robbins case 'c': 671c5604d0aSTim J. Robbins if (flags & LONGINT) 672c5604d0aSTim J. Robbins *(cp = buf) = (wchar_t)GETARG(wint_t); 673c5604d0aSTim J. Robbins else 674c5604d0aSTim J. Robbins *(cp = buf) = (wchar_t)btowc(GETARG(int)); 675c5604d0aSTim J. Robbins size = 1; 676c5604d0aSTim J. Robbins sign = '\0'; 677c5604d0aSTim J. Robbins break; 678c5604d0aSTim J. Robbins case 'D': 679c5604d0aSTim J. Robbins flags |= LONGINT; 680c5604d0aSTim J. Robbins /*FALLTHROUGH*/ 681c5604d0aSTim J. Robbins case 'd': 682c5604d0aSTim J. Robbins case 'i': 683c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) { 684c5604d0aSTim J. Robbins ujval = SJARG(); 685c5604d0aSTim J. Robbins if ((intmax_t)ujval < 0) { 686c5604d0aSTim J. Robbins ujval = -ujval; 687c5604d0aSTim J. Robbins sign = '-'; 688c5604d0aSTim J. Robbins } 689c5604d0aSTim J. Robbins } else { 690c5604d0aSTim J. Robbins ulval = SARG(); 691c5604d0aSTim J. Robbins if ((long)ulval < 0) { 692c5604d0aSTim J. Robbins ulval = -ulval; 693c5604d0aSTim J. Robbins sign = '-'; 694c5604d0aSTim J. Robbins } 695c5604d0aSTim J. Robbins } 696c5604d0aSTim J. Robbins base = 10; 697c5604d0aSTim J. Robbins goto number; 698ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT 699c5604d0aSTim J. Robbins case 'a': 700c5604d0aSTim J. Robbins case 'A': 701adfd6b31STim J. Robbins if (ch == 'a') { 702adfd6b31STim J. Robbins ox[1] = 'x'; 703adfd6b31STim J. Robbins xdigs = xdigs_lower; 704adfd6b31STim J. Robbins expchar = 'p'; 705adfd6b31STim J. Robbins } else { 706adfd6b31STim J. Robbins ox[1] = 'X'; 707adfd6b31STim J. Robbins xdigs = xdigs_upper; 708adfd6b31STim J. Robbins expchar = 'P'; 709adfd6b31STim J. Robbins } 7107b7e3509SDavid Schultz if (prec >= 0) 7117b7e3509SDavid Schultz prec++; 712adfd6b31STim J. Robbins if (flags & LONGDBL) { 7137b7e3509SDavid Schultz fparg.ldbl = GETARG(long double); 714adfd6b31STim J. Robbins dtoaresult = 715adfd6b31STim J. Robbins __hldtoa(fparg.ldbl, xdigs, prec, 716adfd6b31STim J. Robbins &expt, &signflag, &dtoaend); 717adfd6b31STim J. Robbins } else { 718adfd6b31STim J. Robbins fparg.dbl = GETARG(double); 719adfd6b31STim J. Robbins dtoaresult = 720adfd6b31STim J. Robbins __hdtoa(fparg.dbl, xdigs, prec, 721adfd6b31STim J. Robbins &expt, &signflag, &dtoaend); 722adfd6b31STim J. Robbins } 7237b7e3509SDavid Schultz if (prec < 0) 7247b7e3509SDavid Schultz prec = dtoaend - dtoaresult; 7257b7e3509SDavid Schultz if (expt == INT_MAX) 7267b7e3509SDavid Schultz ox[1] = '\0'; 727adfd6b31STim J. Robbins if (convbuf != NULL) 728adfd6b31STim J. Robbins free(convbuf); 7297b7e3509SDavid Schultz ndig = dtoaend - dtoaresult; 730adfd6b31STim J. Robbins cp = convbuf = __mbsconv(dtoaresult, -1); 731adfd6b31STim J. Robbins freedtoa(dtoaresult); 7327b7e3509SDavid Schultz goto fp_common; 733c5604d0aSTim J. Robbins case 'e': 734c5604d0aSTim J. Robbins case 'E': 735adfd6b31STim J. Robbins expchar = ch; 736adfd6b31STim J. Robbins if (prec < 0) /* account for digit before decpt */ 737adfd6b31STim J. Robbins prec = DEFPREC + 1; 738adfd6b31STim J. Robbins else 739adfd6b31STim J. Robbins prec++; 740adfd6b31STim J. Robbins goto fp_begin; 741c5604d0aSTim J. Robbins case 'f': 742c5604d0aSTim J. Robbins case 'F': 743adfd6b31STim J. Robbins expchar = '\0'; 744c5604d0aSTim J. Robbins goto fp_begin; 745c5604d0aSTim J. Robbins case 'g': 746c5604d0aSTim J. Robbins case 'G': 747adfd6b31STim J. Robbins expchar = ch - ('g' - 'e'); 748c5604d0aSTim J. Robbins if (prec == 0) 749c5604d0aSTim J. Robbins prec = 1; 750adfd6b31STim J. Robbins fp_begin: 751adfd6b31STim J. Robbins if (prec < 0) 752c5604d0aSTim J. Robbins prec = DEFPREC; 753adfd6b31STim J. Robbins if (convbuf != NULL) 754adfd6b31STim J. Robbins free(convbuf); 755adfd6b31STim J. Robbins if (flags & LONGDBL) { 756adfd6b31STim J. Robbins fparg.ldbl = GETARG(long double); 757adfd6b31STim J. Robbins dtoaresult = 758adfd6b31STim J. Robbins __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, 759adfd6b31STim J. Robbins &expt, &signflag, &dtoaend); 760adfd6b31STim J. Robbins } else { 761adfd6b31STim J. Robbins fparg.dbl = GETARG(double); 762adfd6b31STim J. Robbins dtoaresult = 763adfd6b31STim J. Robbins dtoa(fparg.dbl, expchar ? 2 : 3, prec, 764adfd6b31STim J. Robbins &expt, &signflag, &dtoaend); 765adfd6b31STim J. Robbins if (expt == 9999) 766adfd6b31STim J. Robbins expt = INT_MAX; 767c5604d0aSTim J. Robbins } 768adfd6b31STim J. Robbins ndig = dtoaend - dtoaresult; 769adfd6b31STim J. Robbins cp = convbuf = __mbsconv(dtoaresult, -1); 770adfd6b31STim J. Robbins freedtoa(dtoaresult); 7717b7e3509SDavid Schultz fp_common: 772adfd6b31STim J. Robbins if (signflag) 773adfd6b31STim J. Robbins sign = '-'; 774adfd6b31STim J. Robbins if (expt == INT_MAX) { /* inf or nan */ 775adfd6b31STim J. Robbins if (*cp == 'N') { 776adfd6b31STim J. Robbins cp = (ch >= 'a') ? L"nan" : L"NAN"; 777adfd6b31STim J. Robbins sign = '\0'; 778adfd6b31STim J. Robbins } else 779adfd6b31STim J. Robbins cp = (ch >= 'a') ? L"inf" : L"INF"; 780c5604d0aSTim J. Robbins size = 3; 7818da510f8SDavid Schultz flags &= ~ZEROPAD; 782c5604d0aSTim J. Robbins break; 783c5604d0aSTim J. Robbins } 784c5604d0aSTim J. Robbins flags |= FPT; 785c5604d0aSTim J. Robbins if (ch == 'g' || ch == 'G') { 786adfd6b31STim J. Robbins if (expt > -4 && expt <= prec) { 787adfd6b31STim J. Robbins /* Make %[gG] smell like %[fF] */ 788adfd6b31STim J. Robbins expchar = '\0'; 789adfd6b31STim J. Robbins if (flags & ALT) 790adfd6b31STim J. Robbins prec -= expt; 791c5604d0aSTim J. Robbins else 792adfd6b31STim J. Robbins prec = ndig - expt; 793adfd6b31STim J. Robbins if (prec < 0) 794adfd6b31STim J. Robbins prec = 0; 7951f2a0cdfSDavid Schultz } else { 7961f2a0cdfSDavid Schultz /* 7971f2a0cdfSDavid Schultz * Make %[gG] smell like %[eE], but 7981f2a0cdfSDavid Schultz * trim trailing zeroes if no # flag. 7991f2a0cdfSDavid Schultz */ 8001f2a0cdfSDavid Schultz if (!(flags & ALT)) 8011f2a0cdfSDavid Schultz prec = ndig; 802c5604d0aSTim J. Robbins } 803adfd6b31STim J. Robbins } 804adfd6b31STim J. Robbins if (expchar) { 805adfd6b31STim J. Robbins expsize = exponent(expstr, expt - 1, expchar); 806adfd6b31STim J. Robbins size = expsize + prec; 807adfd6b31STim J. Robbins if (prec > 1 || flags & ALT) 808c5604d0aSTim J. Robbins ++size; 809adfd6b31STim J. Robbins } else { 810d73c448bSTim J. Robbins /* space for digits before decimal point */ 811d73c448bSTim J. Robbins if (expt > 0) 812c5604d0aSTim J. Robbins size = expt; 813d73c448bSTim J. Robbins else /* "0" */ 814d73c448bSTim J. Robbins size = 1; 815d73c448bSTim J. Robbins /* space for decimal pt and following digits */ 816c5604d0aSTim J. Robbins if (prec || flags & ALT) 817c5604d0aSTim J. Robbins size += prec + 1; 81821ca178eSDavid Schultz if ((flags & GROUPING) && expt > 0) 81921ca178eSDavid Schultz size += grouping_init(&gs, expt); 820adfd6b31STim J. Robbins } 821c5604d0aSTim J. Robbins break; 822ce2551adSDavid Schultz #endif /* !NO_FLOATING_POINT */ 823c5604d0aSTim J. Robbins case 'n': 824c5604d0aSTim J. Robbins /* 825c5604d0aSTim J. Robbins * Assignment-like behavior is specified if the 826c5604d0aSTim J. Robbins * value overflows or is otherwise unrepresentable. 827c5604d0aSTim J. Robbins * C99 says to use `signed char' for %hhn conversions. 828c5604d0aSTim J. Robbins */ 829c5604d0aSTim J. Robbins if (flags & LLONGINT) 830c5604d0aSTim J. Robbins *GETARG(long long *) = ret; 831c5604d0aSTim J. Robbins else if (flags & SIZET) 832c5604d0aSTim J. Robbins *GETARG(ssize_t *) = (ssize_t)ret; 833c5604d0aSTim J. Robbins else if (flags & PTRDIFFT) 834c5604d0aSTim J. Robbins *GETARG(ptrdiff_t *) = ret; 835c5604d0aSTim J. Robbins else if (flags & INTMAXT) 836c5604d0aSTim J. Robbins *GETARG(intmax_t *) = ret; 837c5604d0aSTim J. Robbins else if (flags & LONGINT) 838c5604d0aSTim J. Robbins *GETARG(long *) = ret; 839c5604d0aSTim J. Robbins else if (flags & SHORTINT) 840c5604d0aSTim J. Robbins *GETARG(short *) = ret; 841c5604d0aSTim J. Robbins else if (flags & CHARINT) 842c5604d0aSTim J. Robbins *GETARG(signed char *) = ret; 843c5604d0aSTim J. Robbins else 844c5604d0aSTim J. Robbins *GETARG(int *) = ret; 845c5604d0aSTim J. Robbins continue; /* no output */ 846c5604d0aSTim J. Robbins case 'O': 847c5604d0aSTim J. Robbins flags |= LONGINT; 848c5604d0aSTim J. Robbins /*FALLTHROUGH*/ 849c5604d0aSTim J. Robbins case 'o': 850c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) 851c5604d0aSTim J. Robbins ujval = UJARG(); 852c5604d0aSTim J. Robbins else 853c5604d0aSTim J. Robbins ulval = UARG(); 854c5604d0aSTim J. Robbins base = 8; 855c5604d0aSTim J. Robbins goto nosign; 856c5604d0aSTim J. Robbins case 'p': 857c5604d0aSTim J. Robbins /*- 858c5604d0aSTim J. Robbins * ``The argument shall be a pointer to void. The 859c5604d0aSTim J. Robbins * value of the pointer is converted to a sequence 860c5604d0aSTim J. Robbins * of printable characters, in an implementation- 861c5604d0aSTim J. Robbins * defined manner.'' 862c5604d0aSTim J. Robbins * -- ANSI X3J11 863c5604d0aSTim J. Robbins */ 864c5604d0aSTim J. Robbins ujval = (uintmax_t)(uintptr_t)GETARG(void *); 865c5604d0aSTim J. Robbins base = 16; 866adfd6b31STim J. Robbins xdigs = xdigs_lower; 867adfd6b31STim J. Robbins flags = flags | INTMAXT; 868adfd6b31STim J. Robbins ox[1] = 'x'; 869c5604d0aSTim J. Robbins goto nosign; 870927ecbf3STim J. Robbins case 'S': 871927ecbf3STim J. Robbins flags |= LONGINT; 872927ecbf3STim J. Robbins /*FALLTHROUGH*/ 873c5604d0aSTim J. Robbins case 's': 874c5604d0aSTim J. Robbins if (flags & LONGINT) { 875c5604d0aSTim J. Robbins if ((cp = GETARG(wchar_t *)) == NULL) 876c5604d0aSTim J. Robbins cp = L"(null)"; 877c5604d0aSTim J. Robbins } else { 878c5604d0aSTim J. Robbins char *mbp; 879c5604d0aSTim J. Robbins 880c5604d0aSTim J. Robbins if (convbuf != NULL) 881c5604d0aSTim J. Robbins free(convbuf); 882c5604d0aSTim J. Robbins if ((mbp = GETARG(char *)) == NULL) 883c5604d0aSTim J. Robbins cp = L"(null)"; 884c5604d0aSTim J. Robbins else { 885c5604d0aSTim J. Robbins convbuf = __mbsconv(mbp, prec); 8866180233fSTim J. Robbins if (convbuf == NULL) { 8876180233fSTim J. Robbins fp->_flags |= __SERR; 888c5604d0aSTim J. Robbins goto error; 8896180233fSTim J. Robbins } 890c5604d0aSTim J. Robbins cp = convbuf; 891c5604d0aSTim J. Robbins } 892c5604d0aSTim J. Robbins } 893c5604d0aSTim J. Robbins 894c5604d0aSTim J. Robbins if (prec >= 0) { 895c5604d0aSTim J. Robbins /* 896c5604d0aSTim J. Robbins * can't use wcslen; can only look for the 897c5604d0aSTim J. Robbins * NUL in the first `prec' characters, and 898c5604d0aSTim J. Robbins * wcslen() will go further. 899c5604d0aSTim J. Robbins */ 900c5604d0aSTim J. Robbins wchar_t *p = wmemchr(cp, 0, (size_t)prec); 901c5604d0aSTim J. Robbins 902c5604d0aSTim J. Robbins if (p != NULL) { 903c5604d0aSTim J. Robbins size = p - cp; 904c5604d0aSTim J. Robbins if (size > prec) 905c5604d0aSTim J. Robbins size = prec; 906c5604d0aSTim J. Robbins } else 907c5604d0aSTim J. Robbins size = prec; 908c5604d0aSTim J. Robbins } else 909c5604d0aSTim J. Robbins size = wcslen(cp); 910c5604d0aSTim J. Robbins sign = '\0'; 911c5604d0aSTim J. Robbins break; 912c5604d0aSTim J. Robbins case 'U': 913c5604d0aSTim J. Robbins flags |= LONGINT; 914c5604d0aSTim J. Robbins /*FALLTHROUGH*/ 915c5604d0aSTim J. Robbins case 'u': 916c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) 917c5604d0aSTim J. Robbins ujval = UJARG(); 918c5604d0aSTim J. Robbins else 919c5604d0aSTim J. Robbins ulval = UARG(); 920c5604d0aSTim J. Robbins base = 10; 921c5604d0aSTim J. Robbins goto nosign; 922c5604d0aSTim J. Robbins case 'X': 923adfd6b31STim J. Robbins xdigs = xdigs_upper; 924c5604d0aSTim J. Robbins goto hex; 925c5604d0aSTim J. Robbins case 'x': 926adfd6b31STim J. Robbins xdigs = xdigs_lower; 927c5604d0aSTim J. Robbins hex: 928c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) 929c5604d0aSTim J. Robbins ujval = UJARG(); 930c5604d0aSTim J. Robbins else 931c5604d0aSTim J. Robbins ulval = UARG(); 932c5604d0aSTim J. Robbins base = 16; 933c5604d0aSTim J. Robbins /* leading 0x/X only if non-zero */ 934c5604d0aSTim J. Robbins if (flags & ALT && 935c5604d0aSTim J. Robbins (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) 936adfd6b31STim J. Robbins ox[1] = ch; 937c5604d0aSTim J. Robbins 938c5604d0aSTim J. Robbins flags &= ~GROUPING; 939c5604d0aSTim J. Robbins /* unsigned conversions */ 940c5604d0aSTim J. Robbins nosign: sign = '\0'; 941c5604d0aSTim J. Robbins /*- 942c5604d0aSTim J. Robbins * ``... diouXx conversions ... if a precision is 943c5604d0aSTim J. Robbins * specified, the 0 flag will be ignored.'' 944c5604d0aSTim J. Robbins * -- ANSI X3J11 945c5604d0aSTim J. Robbins */ 946c5604d0aSTim J. Robbins number: if ((dprec = prec) >= 0) 947c5604d0aSTim J. Robbins flags &= ~ZEROPAD; 948c5604d0aSTim J. Robbins 949c5604d0aSTim J. Robbins /*- 950c5604d0aSTim J. Robbins * ``The result of converting a zero value with an 951c5604d0aSTim J. Robbins * explicit precision of zero is no characters.'' 952c5604d0aSTim J. Robbins * -- ANSI X3J11 9531be5319aSDavid Schultz * 9541be5319aSDavid Schultz * ``The C Standard is clear enough as is. The call 9551be5319aSDavid Schultz * printf("%#.0o", 0) should print 0.'' 9561be5319aSDavid Schultz * -- Defect Report #151 957c5604d0aSTim J. Robbins */ 958c5604d0aSTim J. Robbins cp = buf + BUF; 959c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) { 9601be5319aSDavid Schultz if (ujval != 0 || prec != 0 || 9611be5319aSDavid Schultz (flags & ALT && base == 8)) 962c5604d0aSTim J. Robbins cp = __ujtoa(ujval, cp, base, 96321ca178eSDavid Schultz flags & ALT, xdigs); 964c5604d0aSTim J. Robbins } else { 9651be5319aSDavid Schultz if (ulval != 0 || prec != 0 || 9661be5319aSDavid Schultz (flags & ALT && base == 8)) 967c5604d0aSTim J. Robbins cp = __ultoa(ulval, cp, base, 96821ca178eSDavid Schultz flags & ALT, xdigs); 969c5604d0aSTim J. Robbins } 970c5604d0aSTim J. Robbins size = buf + BUF - cp; 97138cac8f8SDavid Schultz if (size > BUF) /* should never happen */ 97238cac8f8SDavid Schultz abort(); 97321ca178eSDavid Schultz if ((flags & GROUPING) && size != 0) 97421ca178eSDavid Schultz size += grouping_init(&gs, size); 975c5604d0aSTim J. Robbins break; 976c5604d0aSTim J. Robbins default: /* "%?" prints ?, unless ? is NUL */ 977c5604d0aSTim J. Robbins if (ch == '\0') 978c5604d0aSTim J. Robbins goto done; 979c5604d0aSTim J. Robbins /* pretend it was %c with argument ch */ 980c5604d0aSTim J. Robbins cp = buf; 981c5604d0aSTim J. Robbins *cp = ch; 982c5604d0aSTim J. Robbins size = 1; 983c5604d0aSTim J. Robbins sign = '\0'; 984c5604d0aSTim J. Robbins break; 985c5604d0aSTim J. Robbins } 986c5604d0aSTim J. Robbins 987c5604d0aSTim J. Robbins /* 988c5604d0aSTim J. Robbins * All reasonable formats wind up here. At this point, `cp' 989c5604d0aSTim J. Robbins * points to a string which (if not flags&LADJUST) should be 990c5604d0aSTim J. Robbins * padded out to `width' places. If flags&ZEROPAD, it should 991c5604d0aSTim J. Robbins * first be prefixed by any sign or other prefix; otherwise, 992c5604d0aSTim J. Robbins * it should be blank padded before the prefix is emitted. 993c5604d0aSTim J. Robbins * After any left-hand padding and prefixing, emit zeroes 994c5604d0aSTim J. Robbins * required by a decimal [diouxX] precision, then print the 995c5604d0aSTim J. Robbins * string proper, then emit zeroes required by any leftover 996c5604d0aSTim J. Robbins * floating precision; finally, if LADJUST, pad with blanks. 997c5604d0aSTim J. Robbins * 998c5604d0aSTim J. Robbins * Compute actual size, so we know how much to pad. 999c5604d0aSTim J. Robbins * size excludes decimal prec; realsz includes it. 1000c5604d0aSTim J. Robbins */ 1001c5604d0aSTim J. Robbins realsz = dprec > size ? dprec : size; 1002c5604d0aSTim J. Robbins if (sign) 1003c5604d0aSTim J. Robbins realsz++; 10047b7e3509SDavid Schultz if (ox[1]) 1005c5604d0aSTim J. Robbins realsz += 2; 1006c5604d0aSTim J. Robbins 1007c5604d0aSTim J. Robbins prsize = width > realsz ? width : realsz; 1008c5604d0aSTim J. Robbins if ((unsigned)ret + prsize > INT_MAX) { 1009c5604d0aSTim J. Robbins ret = EOF; 1010c5604d0aSTim J. Robbins goto error; 1011c5604d0aSTim J. Robbins } 1012c5604d0aSTim J. Robbins 1013c5604d0aSTim J. Robbins /* right-adjusting blank padding */ 1014c5604d0aSTim J. Robbins if ((flags & (LADJUST|ZEROPAD)) == 0) 1015c5604d0aSTim J. Robbins PAD(width - realsz, blanks); 1016c5604d0aSTim J. Robbins 1017c5604d0aSTim J. Robbins /* prefix */ 10187b7e3509SDavid Schultz if (sign) 1019c5604d0aSTim J. Robbins PRINT(&sign, 1); 10207b7e3509SDavid Schultz 10217b7e3509SDavid Schultz if (ox[1]) { /* ox[1] is either x, X, or \0 */ 1022c5604d0aSTim J. Robbins ox[0] = '0'; 1023c5604d0aSTim J. Robbins PRINT(ox, 2); 1024c5604d0aSTim J. Robbins } 1025c5604d0aSTim J. Robbins 1026c5604d0aSTim J. Robbins /* right-adjusting zero padding */ 1027c5604d0aSTim J. Robbins if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 1028c5604d0aSTim J. Robbins PAD(width - realsz, zeroes); 1029c5604d0aSTim J. Robbins 1030c5604d0aSTim J. Robbins /* the string or number proper */ 1031ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT 1032c5604d0aSTim J. Robbins if ((flags & FPT) == 0) { 103321ca178eSDavid Schultz #endif 103421ca178eSDavid Schultz /* leading zeroes from decimal precision */ 103521ca178eSDavid Schultz PAD(dprec - size, zeroes); 103621ca178eSDavid Schultz if (gs.grouping) { 103721ca178eSDavid Schultz if (grouping_print(&gs, &io, cp, buf+BUF) < 0) 103821ca178eSDavid Schultz goto error; 103921ca178eSDavid Schultz } else { 1040c5604d0aSTim J. Robbins PRINT(cp, size); 104121ca178eSDavid Schultz } 104221ca178eSDavid Schultz #ifndef NO_FLOATING_POINT 1043c5604d0aSTim J. Robbins } else { /* glue together f_p fragments */ 1044adfd6b31STim J. Robbins if (!expchar) { /* %[fF] or sufficiently short %[gG] */ 1045adfd6b31STim J. Robbins if (expt <= 0) { 1046d73c448bSTim J. Robbins PRINT(zeroes, 1); 10475004a238SDavid Schultz if (prec || flags & ALT) 10485004a238SDavid Schultz PRINT(&decimal_point, 1); 1049c5604d0aSTim J. Robbins PAD(-expt, zeroes); 1050adfd6b31STim J. Robbins /* already handled initial 0's */ 1051adfd6b31STim J. Robbins prec += expt; 1052c5604d0aSTim J. Robbins } else { 105321ca178eSDavid Schultz if (gs.grouping) { 105421ca178eSDavid Schultz n = grouping_print(&gs, &io, 105521ca178eSDavid Schultz cp, convbuf + ndig); 105621ca178eSDavid Schultz if (n < 0) 105721ca178eSDavid Schultz goto error; 105821ca178eSDavid Schultz cp += n; 105921ca178eSDavid Schultz } else { 106021ca178eSDavid Schultz PRINTANDPAD(cp, convbuf + ndig, 106121ca178eSDavid Schultz expt, zeroes); 106221ca178eSDavid Schultz cp += expt; 1063adfd6b31STim J. Robbins } 10645004a238SDavid Schultz if (prec || flags & ALT) 10655004a238SDavid Schultz PRINT(&decimal_point, 1); 1066adfd6b31STim J. Robbins } 1067adfd6b31STim J. Robbins PRINTANDPAD(cp, convbuf + ndig, prec, zeroes); 1068adfd6b31STim J. Robbins } else { /* %[eE] or sufficiently long %[gG] */ 1069adfd6b31STim J. Robbins if (prec > 1 || flags & ALT) { 1070adfd6b31STim J. Robbins buf[0] = *cp++; 10715004a238SDavid Schultz buf[1] = decimal_point; 1072adfd6b31STim J. Robbins PRINT(buf, 2); 1073c5604d0aSTim J. Robbins PRINT(cp, ndig-1); 1074adfd6b31STim J. Robbins PAD(prec - ndig, zeroes); 1075c5604d0aSTim J. Robbins } else /* XeYYY */ 1076c5604d0aSTim J. Robbins PRINT(cp, 1); 1077c5604d0aSTim J. Robbins PRINT(expstr, expsize); 1078c5604d0aSTim J. Robbins } 1079c5604d0aSTim J. Robbins } 1080c5604d0aSTim J. Robbins #endif 1081c5604d0aSTim J. Robbins /* left-adjusting padding (always blank) */ 1082c5604d0aSTim J. Robbins if (flags & LADJUST) 1083c5604d0aSTim J. Robbins PAD(width - realsz, blanks); 1084c5604d0aSTim J. Robbins 1085c5604d0aSTim J. Robbins /* finally, adjust ret */ 1086c5604d0aSTim J. Robbins ret += prsize; 1087814d1bc9SDavid Schultz 1088814d1bc9SDavid Schultz FLUSH(); /* copy out the I/O vectors */ 1089c5604d0aSTim J. Robbins } 1090c5604d0aSTim J. Robbins done: 1091814d1bc9SDavid Schultz FLUSH(); 1092c5604d0aSTim J. Robbins error: 1093096ad104SDag-Erling Smørgrav va_end(orgap); 1094c5604d0aSTim J. Robbins if (convbuf != NULL) 1095c5604d0aSTim J. Robbins free(convbuf); 1096c5604d0aSTim J. Robbins if (__sferror(fp)) 1097c5604d0aSTim J. Robbins ret = EOF; 1098c5604d0aSTim J. Robbins if ((argtable != NULL) && (argtable != statargtable)) 1099c5604d0aSTim J. Robbins free (argtable); 1100c5604d0aSTim J. Robbins return (ret); 1101c5604d0aSTim J. Robbins /* NOTREACHED */ 1102c5604d0aSTim J. Robbins } 1103