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 * 8*3c87aa1dSDavid Chisnall * Copyright (c) 2011 The FreeBSD Foundation 9*3c87aa1dSDavid Chisnall * All rights reserved. 10*3c87aa1dSDavid Chisnall * Portions of this software were developed by David Chisnall 11*3c87aa1dSDavid Chisnall * under sponsorship from the FreeBSD Foundation. 12*3c87aa1dSDavid Chisnall * 13c5604d0aSTim J. Robbins * Redistribution and use in source and binary forms, with or without 14c5604d0aSTim J. Robbins * modification, are permitted provided that the following conditions 15c5604d0aSTim J. Robbins * are met: 16c5604d0aSTim J. Robbins * 1. Redistributions of source code must retain the above copyright 17c5604d0aSTim J. Robbins * notice, this list of conditions and the following disclaimer. 18c5604d0aSTim J. Robbins * 2. Redistributions in binary form must reproduce the above copyright 19c5604d0aSTim J. Robbins * notice, this list of conditions and the following disclaimer in the 20c5604d0aSTim J. Robbins * documentation and/or other materials provided with the distribution. 21c5604d0aSTim J. Robbins * 4. Neither the name of the University nor the names of its contributors 22c5604d0aSTim J. Robbins * may be used to endorse or promote products derived from this software 23c5604d0aSTim J. Robbins * without specific prior written permission. 24c5604d0aSTim J. Robbins * 25c5604d0aSTim J. Robbins * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26c5604d0aSTim J. Robbins * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27c5604d0aSTim J. Robbins * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28c5604d0aSTim J. Robbins * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29c5604d0aSTim J. Robbins * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30c5604d0aSTim J. Robbins * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31c5604d0aSTim J. Robbins * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32c5604d0aSTim J. Robbins * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33c5604d0aSTim J. Robbins * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34c5604d0aSTim J. Robbins * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35c5604d0aSTim J. Robbins * SUCH DAMAGE. 36c5604d0aSTim J. Robbins */ 37c5604d0aSTim J. Robbins 38c5604d0aSTim J. Robbins #if 0 39c5604d0aSTim J. Robbins #if defined(LIBC_SCCS) && !defined(lint) 40c5604d0aSTim J. Robbins static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; 41c5604d0aSTim J. Robbins #endif /* LIBC_SCCS and not lint */ 42c5604d0aSTim J. Robbins #endif 43af152640SDavid E. O'Brien #include <sys/cdefs.h> 44c5604d0aSTim J. Robbins __FBSDID("$FreeBSD$"); 45c5604d0aSTim J. Robbins 46c5604d0aSTim J. Robbins /* 47c5604d0aSTim J. Robbins * Actual wprintf innards. 48c5604d0aSTim J. Robbins * 49c5604d0aSTim J. Robbins * Avoid making gratuitous changes to this source file; it should be kept 50c5604d0aSTim J. Robbins * as close as possible to vfprintf.c for ease of maintenance. 51c5604d0aSTim J. Robbins */ 52c5604d0aSTim J. Robbins 53c5604d0aSTim J. Robbins #include "namespace.h" 54c5604d0aSTim J. Robbins #include <sys/types.h> 55c5604d0aSTim J. Robbins 56c5604d0aSTim J. Robbins #include <ctype.h> 57c5604d0aSTim J. Robbins #include <limits.h> 58c5604d0aSTim J. Robbins #include <locale.h> 59c5604d0aSTim J. Robbins #include <stdarg.h> 60c5604d0aSTim J. Robbins #include <stddef.h> 61c5604d0aSTim J. Robbins #include <stdint.h> 62c5604d0aSTim J. Robbins #include <stdio.h> 63c5604d0aSTim J. Robbins #include <stdlib.h> 64c5604d0aSTim J. Robbins #include <string.h> 65c5604d0aSTim J. Robbins #include <wchar.h> 66c5604d0aSTim J. Robbins #include <wctype.h> 67c5604d0aSTim J. Robbins #include "un-namespace.h" 68c5604d0aSTim J. Robbins 69c5604d0aSTim J. Robbins #include "libc_private.h" 70c5604d0aSTim J. Robbins #include "local.h" 71c5604d0aSTim J. Robbins #include "fvwrite.h" 722591efccSDavid Schultz #include "printflocal.h" 73*3c87aa1dSDavid Chisnall #include "xlocale_private.h" 74e5abb5e6SDavid Schultz 75*3c87aa1dSDavid Chisnall static int __sprint(FILE *, struct __suio *, locale_t); 76*3c87aa1dSDavid Chisnall static int __sbprintf(FILE *, locale_t, const wchar_t *, va_list) __noinline; 77*3c87aa1dSDavid Chisnall static wint_t __xfputwc(wchar_t, FILE *, locale_t); 78c5604d0aSTim J. Robbins static wchar_t *__mbsconv(char *, int); 79c5604d0aSTim J. Robbins 80814d1bc9SDavid Schultz #define CHAR wchar_t 81814d1bc9SDavid Schultz #include "printfcommon.h" 82814d1bc9SDavid Schultz 8321ca178eSDavid Schultz struct grouping_state { 8421ca178eSDavid Schultz wchar_t thousands_sep; /* locale-specific thousands separator */ 8521ca178eSDavid Schultz const char *grouping; /* locale-specific numeric grouping rules */ 8621ca178eSDavid Schultz int lead; /* sig figs before decimal or group sep */ 8721ca178eSDavid Schultz int nseps; /* number of group separators with ' */ 8821ca178eSDavid Schultz int nrepeats; /* number of repeats of the last group */ 8921ca178eSDavid Schultz }; 9021ca178eSDavid Schultz 915004a238SDavid Schultz static const mbstate_t initial_mbs; 925004a238SDavid Schultz 935004a238SDavid Schultz static inline wchar_t 94*3c87aa1dSDavid Chisnall get_decpt(locale_t locale) 955004a238SDavid Schultz { 965004a238SDavid Schultz mbstate_t mbs; 975004a238SDavid Schultz wchar_t decpt; 985004a238SDavid Schultz int nconv; 995004a238SDavid Schultz 1005004a238SDavid Schultz mbs = initial_mbs; 101*3c87aa1dSDavid Chisnall nconv = mbrtowc(&decpt, localeconv_l(locale)->decimal_point, MB_CUR_MAX, &mbs); 1025004a238SDavid Schultz if (nconv == (size_t)-1 || nconv == (size_t)-2) 1035004a238SDavid Schultz decpt = '.'; /* failsafe */ 1045004a238SDavid Schultz return (decpt); 1055004a238SDavid Schultz } 1065004a238SDavid Schultz 10721ca178eSDavid Schultz static inline wchar_t 108*3c87aa1dSDavid Chisnall get_thousep(locale_t locale) 10921ca178eSDavid Schultz { 11021ca178eSDavid Schultz mbstate_t mbs; 11121ca178eSDavid Schultz wchar_t thousep; 11221ca178eSDavid Schultz int nconv; 11321ca178eSDavid Schultz 11421ca178eSDavid Schultz mbs = initial_mbs; 115*3c87aa1dSDavid Chisnall nconv = mbrtowc(&thousep, localeconv_l(locale)->thousands_sep, 11621ca178eSDavid Schultz MB_CUR_MAX, &mbs); 11721ca178eSDavid Schultz if (nconv == (size_t)-1 || nconv == (size_t)-2) 11821ca178eSDavid Schultz thousep = '\0'; /* failsafe */ 11921ca178eSDavid Schultz return (thousep); 12021ca178eSDavid Schultz } 12121ca178eSDavid Schultz 12221ca178eSDavid Schultz /* 12321ca178eSDavid Schultz * Initialize the thousands' grouping state in preparation to print a 12421ca178eSDavid Schultz * number with ndigits digits. This routine returns the total number 12521ca178eSDavid Schultz * of wide characters that will be printed. 12621ca178eSDavid Schultz */ 12721ca178eSDavid Schultz static int 128*3c87aa1dSDavid Chisnall grouping_init(struct grouping_state *gs, int ndigits, locale_t locale) 12921ca178eSDavid Schultz { 13021ca178eSDavid Schultz 131*3c87aa1dSDavid Chisnall gs->grouping = localeconv_l(locale)->grouping; 132*3c87aa1dSDavid Chisnall gs->thousands_sep = get_thousep(locale); 13321ca178eSDavid Schultz 13421ca178eSDavid Schultz gs->nseps = gs->nrepeats = 0; 13521ca178eSDavid Schultz gs->lead = ndigits; 13621ca178eSDavid Schultz while (*gs->grouping != CHAR_MAX) { 13721ca178eSDavid Schultz if (gs->lead <= *gs->grouping) 13821ca178eSDavid Schultz break; 13921ca178eSDavid Schultz gs->lead -= *gs->grouping; 14021ca178eSDavid Schultz if (*(gs->grouping+1)) { 14121ca178eSDavid Schultz gs->nseps++; 14221ca178eSDavid Schultz gs->grouping++; 14321ca178eSDavid Schultz } else 14421ca178eSDavid Schultz gs->nrepeats++; 14521ca178eSDavid Schultz } 14621ca178eSDavid Schultz return (gs->nseps + gs->nrepeats); 14721ca178eSDavid Schultz } 14821ca178eSDavid Schultz 14921ca178eSDavid Schultz /* 15021ca178eSDavid Schultz * Print a number with thousands' separators. 15121ca178eSDavid Schultz */ 15221ca178eSDavid Schultz static int 15321ca178eSDavid Schultz grouping_print(struct grouping_state *gs, struct io_state *iop, 154*3c87aa1dSDavid Chisnall const CHAR *cp, const CHAR *ep, locale_t locale) 15521ca178eSDavid Schultz { 15621ca178eSDavid Schultz const CHAR *cp0 = cp; 15721ca178eSDavid Schultz 158*3c87aa1dSDavid Chisnall if (io_printandpad(iop, cp, ep, gs->lead, zeroes, locale)) 15921ca178eSDavid Schultz return (-1); 16021ca178eSDavid Schultz cp += gs->lead; 16121ca178eSDavid Schultz while (gs->nseps > 0 || gs->nrepeats > 0) { 16221ca178eSDavid Schultz if (gs->nrepeats > 0) 16321ca178eSDavid Schultz gs->nrepeats--; 16421ca178eSDavid Schultz else { 16521ca178eSDavid Schultz gs->grouping--; 16621ca178eSDavid Schultz gs->nseps--; 16721ca178eSDavid Schultz } 168*3c87aa1dSDavid Chisnall if (io_print(iop, &gs->thousands_sep, 1, locale)) 16921ca178eSDavid Schultz return (-1); 170*3c87aa1dSDavid Chisnall if (io_printandpad(iop, cp, ep, *gs->grouping, zeroes, locale)) 17121ca178eSDavid Schultz return (-1); 17221ca178eSDavid Schultz cp += *gs->grouping; 17321ca178eSDavid Schultz } 17421ca178eSDavid Schultz if (cp > ep) 17521ca178eSDavid Schultz cp = ep; 17621ca178eSDavid Schultz return (cp - cp0); 17721ca178eSDavid Schultz } 17821ca178eSDavid Schultz 17921ca178eSDavid Schultz 180814d1bc9SDavid Schultz /* 181814d1bc9SDavid Schultz * Flush out all the vectors defined by the given uio, 182814d1bc9SDavid Schultz * then reset it so that it can be reused. 183814d1bc9SDavid Schultz * 184814d1bc9SDavid Schultz * XXX The fact that we do this a character at a time and convert to a 185814d1bc9SDavid Schultz * multibyte character sequence even if the destination is a wide 186814d1bc9SDavid Schultz * string eclipses the benefits of buffering. 187814d1bc9SDavid Schultz */ 188814d1bc9SDavid Schultz static int 189*3c87aa1dSDavid Chisnall __sprint(FILE *fp, struct __suio *uio, locale_t locale) 190814d1bc9SDavid Schultz { 191814d1bc9SDavid Schultz struct __siov *iov; 192814d1bc9SDavid Schultz wchar_t *p; 193814d1bc9SDavid Schultz int i, len; 194814d1bc9SDavid Schultz 195814d1bc9SDavid Schultz iov = uio->uio_iov; 196814d1bc9SDavid Schultz for (; uio->uio_resid != 0; uio->uio_resid -= len, iov++) { 197814d1bc9SDavid Schultz p = (wchar_t *)iov->iov_base; 198814d1bc9SDavid Schultz len = iov->iov_len; 199814d1bc9SDavid Schultz for (i = 0; i < len; i++) { 200*3c87aa1dSDavid Chisnall if (__xfputwc(p[i], fp, locale) == WEOF) 201814d1bc9SDavid Schultz return (-1); 202814d1bc9SDavid Schultz } 203814d1bc9SDavid Schultz } 204814d1bc9SDavid Schultz uio->uio_iovcnt = 0; 205814d1bc9SDavid Schultz return (0); 206814d1bc9SDavid Schultz } 207814d1bc9SDavid Schultz 208c5604d0aSTim J. Robbins /* 209c5604d0aSTim J. Robbins * Helper function for `fprintf to unbuffered unix file': creates a 210c5604d0aSTim J. Robbins * temporary buffer. We only work on write-only files; this avoids 211c5604d0aSTim J. Robbins * worries about ungetc buffers and so forth. 212c5604d0aSTim J. Robbins */ 213c5604d0aSTim J. Robbins static int 214*3c87aa1dSDavid Chisnall __sbprintf(FILE *fp, locale_t locale, const wchar_t *fmt, va_list ap) 215c5604d0aSTim J. Robbins { 216c5604d0aSTim J. Robbins int ret; 217c5604d0aSTim J. Robbins FILE fake; 218c5604d0aSTim J. Robbins unsigned char buf[BUFSIZ]; 219c5604d0aSTim J. Robbins 220a1805f7bSDavid Schultz /* XXX This is probably not needed. */ 221a1805f7bSDavid Schultz if (prepwrite(fp) != 0) 222a1805f7bSDavid Schultz return (EOF); 223a1805f7bSDavid Schultz 224c5604d0aSTim J. Robbins /* copy the important variables */ 225c5604d0aSTim J. Robbins fake._flags = fp->_flags & ~__SNBF; 226c5604d0aSTim J. Robbins fake._file = fp->_file; 227c5604d0aSTim J. Robbins fake._cookie = fp->_cookie; 228c5604d0aSTim J. Robbins fake._write = fp->_write; 2291e98f887SJohn Baldwin fake._orientation = fp->_orientation; 2301e98f887SJohn Baldwin fake._mbstate = fp->_mbstate; 231c5604d0aSTim J. Robbins 232c5604d0aSTim J. Robbins /* set up the buffer */ 233c5604d0aSTim J. Robbins fake._bf._base = fake._p = buf; 234c5604d0aSTim J. Robbins fake._bf._size = fake._w = sizeof(buf); 235c5604d0aSTim J. Robbins fake._lbfsize = 0; /* not actually used, but Just In Case */ 236c5604d0aSTim J. Robbins 237c5604d0aSTim J. Robbins /* do the work, then copy any error status */ 238*3c87aa1dSDavid Chisnall ret = __vfwprintf(&fake, locale, fmt, ap); 239c5604d0aSTim J. Robbins if (ret >= 0 && __fflush(&fake)) 240c5604d0aSTim J. Robbins ret = WEOF; 241c5604d0aSTim J. Robbins if (fake._flags & __SERR) 242c5604d0aSTim J. Robbins fp->_flags |= __SERR; 243c5604d0aSTim J. Robbins return (ret); 244c5604d0aSTim J. Robbins } 245c5604d0aSTim J. Robbins 246c5604d0aSTim J. Robbins /* 247909a17f4STim J. Robbins * Like __fputwc, but handles fake string (__SSTR) files properly. 248909a17f4STim J. Robbins * File must already be locked. 249909a17f4STim J. Robbins */ 250909a17f4STim J. Robbins static wint_t 251*3c87aa1dSDavid Chisnall __xfputwc(wchar_t wc, FILE *fp, locale_t locale) 252909a17f4STim J. Robbins { 25393996f6dSTim J. Robbins mbstate_t mbs; 254909a17f4STim J. Robbins char buf[MB_LEN_MAX]; 255909a17f4STim J. Robbins struct __suio uio; 256909a17f4STim J. Robbins struct __siov iov; 25784d9142fSJacques Vidrine size_t len; 258909a17f4STim J. Robbins 259909a17f4STim J. Robbins if ((fp->_flags & __SSTR) == 0) 260*3c87aa1dSDavid Chisnall return (__fputwc(wc, fp, locale)); 261909a17f4STim J. Robbins 2625004a238SDavid Schultz mbs = initial_mbs; 26393996f6dSTim J. Robbins if ((len = wcrtomb(buf, wc, &mbs)) == (size_t)-1) { 264909a17f4STim J. Robbins fp->_flags |= __SERR; 265909a17f4STim J. Robbins return (WEOF); 266909a17f4STim J. Robbins } 267909a17f4STim J. Robbins uio.uio_iov = &iov; 268909a17f4STim J. Robbins uio.uio_resid = len; 269909a17f4STim J. Robbins uio.uio_iovcnt = 1; 270909a17f4STim J. Robbins iov.iov_base = buf; 271909a17f4STim J. Robbins iov.iov_len = len; 272909a17f4STim J. Robbins return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF); 273909a17f4STim J. Robbins } 274909a17f4STim J. Robbins 275909a17f4STim J. Robbins /* 276c5604d0aSTim J. Robbins * Convert a multibyte character string argument for the %s format to a wide 277c5604d0aSTim J. Robbins * string representation. ``prec'' specifies the maximum number of bytes 278c5604d0aSTim J. Robbins * to output. If ``prec'' is greater than or equal to zero, we can't assume 279c5604d0aSTim J. Robbins * that the multibyte char. string ends in a null character. 280c5604d0aSTim J. Robbins */ 281c5604d0aSTim J. Robbins static wchar_t * 282c5604d0aSTim J. Robbins __mbsconv(char *mbsarg, int prec) 283c5604d0aSTim J. Robbins { 28493996f6dSTim J. Robbins mbstate_t mbs; 285c5604d0aSTim J. Robbins wchar_t *convbuf, *wcp; 286c5604d0aSTim J. Robbins const char *p; 287c5604d0aSTim J. Robbins size_t insize, nchars, nconv; 288c5604d0aSTim J. Robbins 289adfd6b31STim J. Robbins if (mbsarg == NULL) 290adfd6b31STim J. Robbins return (NULL); 291adfd6b31STim J. Robbins 292c5604d0aSTim J. Robbins /* 293c5604d0aSTim J. Robbins * Supplied argument is a multibyte string; convert it to wide 294c5604d0aSTim J. Robbins * characters first. 295c5604d0aSTim J. Robbins */ 296c5604d0aSTim J. Robbins if (prec >= 0) { 297c5604d0aSTim J. Robbins /* 298c5604d0aSTim J. Robbins * String is not guaranteed to be NUL-terminated. Find the 299c5604d0aSTim J. Robbins * number of characters to print. 300c5604d0aSTim J. Robbins */ 301c5604d0aSTim J. Robbins p = mbsarg; 302ab5b2fafSGarrett Wollman insize = nchars = nconv = 0; 3035004a238SDavid Schultz mbs = initial_mbs; 304c5604d0aSTim J. Robbins while (nchars != (size_t)prec) { 30593996f6dSTim J. Robbins nconv = mbrlen(p, MB_CUR_MAX, &mbs); 306c5604d0aSTim J. Robbins if (nconv == 0 || nconv == (size_t)-1 || 307c5604d0aSTim J. Robbins nconv == (size_t)-2) 308c5604d0aSTim J. Robbins break; 309c5604d0aSTim J. Robbins p += nconv; 310c5604d0aSTim J. Robbins nchars++; 311c5604d0aSTim J. Robbins insize += nconv; 312c5604d0aSTim J. Robbins } 313c5604d0aSTim J. Robbins if (nconv == (size_t)-1 || nconv == (size_t)-2) 314c5604d0aSTim J. Robbins return (NULL); 31522d725b5SColin Percival } else { 316c5604d0aSTim J. Robbins insize = strlen(mbsarg); 31722d725b5SColin Percival nconv = 0; 31822d725b5SColin Percival } 319c5604d0aSTim J. Robbins 320c5604d0aSTim J. Robbins /* 321c5604d0aSTim J. Robbins * Allocate buffer for the result and perform the conversion, 322c5604d0aSTim J. Robbins * converting at most `size' bytes of the input multibyte string to 323c5604d0aSTim J. Robbins * wide characters for printing. 324c5604d0aSTim J. Robbins */ 325c5604d0aSTim J. Robbins convbuf = malloc((insize + 1) * sizeof(*convbuf)); 326c5604d0aSTim J. Robbins if (convbuf == NULL) 327c5604d0aSTim J. Robbins return (NULL); 328c5604d0aSTim J. Robbins wcp = convbuf; 329c5604d0aSTim J. Robbins p = mbsarg; 3305004a238SDavid Schultz mbs = initial_mbs; 331c5604d0aSTim J. Robbins while (insize != 0) { 33293996f6dSTim J. Robbins nconv = mbrtowc(wcp, p, insize, &mbs); 333c5604d0aSTim J. Robbins if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) 334c5604d0aSTim J. Robbins break; 335c5604d0aSTim J. Robbins wcp++; 336c5604d0aSTim J. Robbins p += nconv; 337c5604d0aSTim J. Robbins insize -= nconv; 338c5604d0aSTim J. Robbins } 339c5604d0aSTim J. Robbins if (nconv == (size_t)-1 || nconv == (size_t)-2) { 340c5604d0aSTim J. Robbins free(convbuf); 341c5604d0aSTim J. Robbins return (NULL); 342c5604d0aSTim J. Robbins } 343c5604d0aSTim J. Robbins *wcp = L'\0'; 344c5604d0aSTim J. Robbins 345c5604d0aSTim J. Robbins return (convbuf); 346c5604d0aSTim J. Robbins } 347c5604d0aSTim J. Robbins 348c5604d0aSTim J. Robbins /* 349c5604d0aSTim J. Robbins * MT-safe version 350c5604d0aSTim J. Robbins */ 351c5604d0aSTim J. Robbins int 352*3c87aa1dSDavid Chisnall vfwprintf_l(FILE * __restrict fp, locale_t locale, 353*3c87aa1dSDavid Chisnall const wchar_t * __restrict fmt0, va_list ap) 354c5604d0aSTim J. Robbins 355c5604d0aSTim J. Robbins { 356c5604d0aSTim J. Robbins int ret; 357*3c87aa1dSDavid Chisnall FIX_LOCALE(locale); 358c5604d0aSTim J. Robbins FLOCKFILE(fp); 359a1805f7bSDavid Schultz /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 360a1805f7bSDavid Schultz if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 361a1805f7bSDavid Schultz fp->_file >= 0) 362*3c87aa1dSDavid Chisnall ret = __sbprintf(fp, locale, fmt0, ap); 363a1805f7bSDavid Schultz else 364*3c87aa1dSDavid Chisnall ret = __vfwprintf(fp, locale, fmt0, ap); 365c5604d0aSTim J. Robbins FUNLOCKFILE(fp); 366c5604d0aSTim J. Robbins return (ret); 367c5604d0aSTim J. Robbins } 368*3c87aa1dSDavid Chisnall int 369*3c87aa1dSDavid Chisnall vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, va_list ap) 370*3c87aa1dSDavid Chisnall { 371*3c87aa1dSDavid Chisnall return vfwprintf_l(fp, __get_locale(), fmt0, ap); 372*3c87aa1dSDavid Chisnall } 373c5604d0aSTim J. Robbins 37438cac8f8SDavid Schultz /* 37538cac8f8SDavid Schultz * The size of the buffer we use as scratch space for integer 37621ca178eSDavid Schultz * conversions, among other things. We need enough space to 37721ca178eSDavid Schultz * write a uintmax_t in octal (plus one byte). 37838cac8f8SDavid Schultz */ 37921ca178eSDavid Schultz #if UINTMAX_MAX <= UINT64_MAX 38021ca178eSDavid Schultz #define BUF 32 38121ca178eSDavid Schultz #else 38221ca178eSDavid Schultz #error "BUF must be large enough to format a uintmax_t" 38321ca178eSDavid Schultz #endif 38438cac8f8SDavid Schultz 385c5604d0aSTim J. Robbins /* 386c5604d0aSTim J. Robbins * Non-MT-safe version 387c5604d0aSTim J. Robbins */ 388c5604d0aSTim J. Robbins int 389*3c87aa1dSDavid Chisnall __vfwprintf(FILE *fp, locale_t locale, const wchar_t *fmt0, va_list ap) 390c5604d0aSTim J. Robbins { 391c5604d0aSTim J. Robbins wchar_t *fmt; /* format string */ 392c5604d0aSTim J. Robbins wchar_t ch; /* character from fmt */ 393814d1bc9SDavid Schultz int n, n2; /* handy integer (short term usage) */ 394c5604d0aSTim J. Robbins wchar_t *cp; /* handy char pointer (short term usage) */ 395c5604d0aSTim J. Robbins int flags; /* flags as above */ 396c5604d0aSTim J. Robbins int ret; /* return value accumulator */ 397c5604d0aSTim J. Robbins int width; /* width from format (%8d), or 0 */ 398adfd6b31STim J. Robbins int prec; /* precision from format; <0 for N/A */ 399c5604d0aSTim J. Robbins wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */ 40021ca178eSDavid Schultz struct grouping_state gs; /* thousands' grouping info */ 401ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT 402adfd6b31STim J. Robbins /* 403adfd6b31STim J. Robbins * We can decompose the printed representation of floating 404adfd6b31STim J. Robbins * point numbers into several parts, some of which may be empty: 405adfd6b31STim J. Robbins * 406adfd6b31STim J. Robbins * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ 407adfd6b31STim J. Robbins * A B ---C--- D E F 408adfd6b31STim J. Robbins * 409adfd6b31STim J. Robbins * A: 'sign' holds this value if present; '\0' otherwise 410adfd6b31STim J. Robbins * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal 411adfd6b31STim J. Robbins * C: cp points to the string MMMNNN. Leading and trailing 412adfd6b31STim J. Robbins * zeros are not in the string and must be added. 413adfd6b31STim J. Robbins * D: expchar holds this character; '\0' if no exponent, e.g. %f 414adfd6b31STim J. Robbins * F: at least two digits for decimal, at least one digit for hex 415adfd6b31STim J. Robbins */ 4165004a238SDavid Schultz wchar_t decimal_point; /* locale specific decimal point */ 417adfd6b31STim J. Robbins int signflag; /* true if float is negative */ 418adfd6b31STim J. Robbins union { /* floating point arguments %[aAeEfFgG] */ 419adfd6b31STim J. Robbins double dbl; 420adfd6b31STim J. Robbins long double ldbl; 421adfd6b31STim J. Robbins } fparg; 422c5604d0aSTim J. Robbins int expt; /* integer value of exponent */ 423adfd6b31STim J. Robbins char expchar; /* exponent character: [eEpP\0] */ 424adfd6b31STim J. Robbins char *dtoaend; /* pointer to end of converted digits */ 425c5604d0aSTim J. Robbins int expsize; /* character count for expstr */ 426adfd6b31STim J. Robbins int ndig; /* actual number of digits returned by dtoa */ 427adfd6b31STim J. Robbins wchar_t expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ 428adfd6b31STim J. Robbins char *dtoaresult; /* buffer allocated by dtoa */ 429c5604d0aSTim J. Robbins #endif 430c5604d0aSTim J. Robbins u_long ulval; /* integer arguments %[diouxX] */ 431c5604d0aSTim J. Robbins uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ 432c5604d0aSTim J. Robbins int base; /* base for [diouxX] conversion */ 433c5604d0aSTim J. Robbins int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 434c5604d0aSTim J. Robbins int realsz; /* field size expanded by dprec, sign, etc */ 435c5604d0aSTim J. Robbins int size; /* size of converted field or string */ 436c5604d0aSTim J. Robbins int prsize; /* max size of printed field */ 4377b7e3509SDavid Schultz const char *xdigs; /* digits for [xX] conversion */ 438814d1bc9SDavid Schultz struct io_state io; /* I/O buffering state */ 43938cac8f8SDavid Schultz wchar_t buf[BUF]; /* buffer with space for digits of uintmax_t */ 440c5604d0aSTim J. Robbins wchar_t ox[2]; /* space for 0x hex-prefix */ 441c5604d0aSTim J. Robbins union arg *argtable; /* args, built due to positional arg */ 442c5604d0aSTim J. Robbins union arg statargtable [STATIC_ARG_TBL_SIZE]; 443c5604d0aSTim J. Robbins int nextarg; /* 1-based argument index */ 444c5604d0aSTim J. Robbins va_list orgap; /* original argument pointer */ 445c5604d0aSTim J. Robbins wchar_t *convbuf; /* multibyte to wide conversion result */ 446c5604d0aSTim J. Robbins 4477b7e3509SDavid Schultz static const char xdigs_lower[16] = "0123456789abcdef"; 4487b7e3509SDavid Schultz static const char xdigs_upper[16] = "0123456789ABCDEF"; 449adfd6b31STim J. Robbins 450814d1bc9SDavid Schultz /* BEWARE, these `goto error' on error. */ 451c5604d0aSTim J. Robbins #define PRINT(ptr, len) do { \ 452*3c87aa1dSDavid Chisnall if (io_print(&io, (ptr), (len), locale)) \ 453814d1bc9SDavid Schultz goto error; \ 454c5604d0aSTim J. Robbins } while (0) 455814d1bc9SDavid Schultz #define PAD(howmany, with) { \ 456*3c87aa1dSDavid Chisnall if (io_pad(&io, (howmany), (with), locale)) \ 457814d1bc9SDavid Schultz goto error; \ 458814d1bc9SDavid Schultz } 459814d1bc9SDavid Schultz #define PRINTANDPAD(p, ep, len, with) { \ 460*3c87aa1dSDavid Chisnall if (io_printandpad(&io, (p), (ep), (len), (with), locale)) \ 461814d1bc9SDavid Schultz goto error; \ 462814d1bc9SDavid Schultz } 463814d1bc9SDavid Schultz #define FLUSH() { \ 464*3c87aa1dSDavid Chisnall if (io_flush(&io, locale)) \ 465814d1bc9SDavid Schultz goto error; \ 466814d1bc9SDavid Schultz } 467c5604d0aSTim J. Robbins 468c5604d0aSTim J. Robbins /* 469c5604d0aSTim J. Robbins * Get the argument indexed by nextarg. If the argument table is 470c5604d0aSTim J. Robbins * built, use it to get the argument. If its not, get the next 471c5604d0aSTim J. Robbins * argument (and arguments must be gotten sequentially). 472c5604d0aSTim J. Robbins */ 473c5604d0aSTim J. Robbins #define GETARG(type) \ 474c5604d0aSTim J. Robbins ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ 475c5604d0aSTim J. Robbins (nextarg++, va_arg(ap, type))) 476c5604d0aSTim J. Robbins 477c5604d0aSTim J. Robbins /* 478c5604d0aSTim J. Robbins * To extend shorts properly, we need both signed and unsigned 479c5604d0aSTim J. Robbins * argument extraction methods. 480c5604d0aSTim J. Robbins */ 481c5604d0aSTim J. Robbins #define SARG() \ 482c5604d0aSTim J. Robbins (flags&LONGINT ? GETARG(long) : \ 483c5604d0aSTim J. Robbins flags&SHORTINT ? (long)(short)GETARG(int) : \ 484c5604d0aSTim J. Robbins flags&CHARINT ? (long)(signed char)GETARG(int) : \ 485c5604d0aSTim J. Robbins (long)GETARG(int)) 486c5604d0aSTim J. Robbins #define UARG() \ 487c5604d0aSTim J. Robbins (flags&LONGINT ? GETARG(u_long) : \ 488c5604d0aSTim J. Robbins flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 489c5604d0aSTim J. Robbins flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ 490c5604d0aSTim J. Robbins (u_long)GETARG(u_int)) 491c5604d0aSTim J. Robbins #define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) 492c5604d0aSTim J. Robbins #define SJARG() \ 493c5604d0aSTim J. Robbins (flags&INTMAXT ? GETARG(intmax_t) : \ 4940881683bSDavid Schultz flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ 495c5604d0aSTim J. Robbins flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ 496c5604d0aSTim J. Robbins (intmax_t)GETARG(long long)) 497c5604d0aSTim J. Robbins #define UJARG() \ 498c5604d0aSTim J. Robbins (flags&INTMAXT ? GETARG(uintmax_t) : \ 499c5604d0aSTim J. Robbins flags&SIZET ? (uintmax_t)GETARG(size_t) : \ 500c5604d0aSTim J. Robbins flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ 501c5604d0aSTim J. Robbins (uintmax_t)GETARG(unsigned long long)) 502c5604d0aSTim J. Robbins 503c5604d0aSTim J. Robbins /* 504c5604d0aSTim J. Robbins * Get * arguments, including the form *nn$. Preserve the nextarg 505c5604d0aSTim J. Robbins * that the argument can be gotten once the type is determined. 506c5604d0aSTim J. Robbins */ 507c5604d0aSTim J. Robbins #define GETASTER(val) \ 508c5604d0aSTim J. Robbins n2 = 0; \ 509c5604d0aSTim J. Robbins cp = fmt; \ 510c5604d0aSTim J. Robbins while (is_digit(*cp)) { \ 511c5604d0aSTim J. Robbins n2 = 10 * n2 + to_digit(*cp); \ 512c5604d0aSTim J. Robbins cp++; \ 513c5604d0aSTim J. Robbins } \ 514c5604d0aSTim J. Robbins if (*cp == '$') { \ 515c5604d0aSTim J. Robbins int hold = nextarg; \ 516c5604d0aSTim J. Robbins if (argtable == NULL) { \ 517c5604d0aSTim J. Robbins argtable = statargtable; \ 518e62e5ff9SDavid Schultz if (__find_warguments (fmt0, orgap, &argtable)) { \ 519e62e5ff9SDavid Schultz ret = EOF; \ 520e62e5ff9SDavid Schultz goto error; \ 521e62e5ff9SDavid Schultz } \ 522c5604d0aSTim J. Robbins } \ 523c5604d0aSTim J. Robbins nextarg = n2; \ 524c5604d0aSTim J. Robbins val = GETARG (int); \ 525c5604d0aSTim J. Robbins nextarg = hold; \ 526c5604d0aSTim J. Robbins fmt = ++cp; \ 527c5604d0aSTim J. Robbins } else { \ 528c5604d0aSTim J. Robbins val = GETARG (int); \ 529c5604d0aSTim J. Robbins } 530c5604d0aSTim J. Robbins 531c5604d0aSTim J. Robbins 532c5604d0aSTim J. Robbins /* sorry, fwprintf(read_only_file, L"") returns WEOF, not 0 */ 53352183d46SDavid Schultz if (prepwrite(fp) != 0) 534c5604d0aSTim J. Robbins return (EOF); 535c5604d0aSTim J. Robbins 536e18701f4SDavid Schultz convbuf = NULL; 537c5604d0aSTim J. Robbins fmt = (wchar_t *)fmt0; 538c5604d0aSTim J. Robbins argtable = NULL; 539c5604d0aSTim J. Robbins nextarg = 1; 540d07090a8STim J. Robbins va_copy(orgap, ap); 541814d1bc9SDavid Schultz io_init(&io, fp); 542c5604d0aSTim J. Robbins ret = 0; 543e18701f4SDavid Schultz #ifndef NO_FLOATING_POINT 544*3c87aa1dSDavid Chisnall decimal_point = get_decpt(locale); 545e18701f4SDavid Schultz #endif 546c5604d0aSTim J. Robbins 547c5604d0aSTim J. Robbins /* 548c5604d0aSTim J. Robbins * Scan the format for conversions (`%' character). 549c5604d0aSTim J. Robbins */ 550c5604d0aSTim J. Robbins for (;;) { 551c5604d0aSTim J. Robbins for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 552c5604d0aSTim J. Robbins /* void */; 553c5604d0aSTim J. Robbins if ((n = fmt - cp) != 0) { 554c5604d0aSTim J. Robbins if ((unsigned)ret + n > INT_MAX) { 555c5604d0aSTim J. Robbins ret = EOF; 556c5604d0aSTim J. Robbins goto error; 557c5604d0aSTim J. Robbins } 558c5604d0aSTim J. Robbins PRINT(cp, n); 559c5604d0aSTim J. Robbins ret += n; 560c5604d0aSTim J. Robbins } 561c5604d0aSTim J. Robbins if (ch == '\0') 562c5604d0aSTim J. Robbins goto done; 563c5604d0aSTim J. Robbins fmt++; /* skip over '%' */ 564c5604d0aSTim J. Robbins 565c5604d0aSTim J. Robbins flags = 0; 566c5604d0aSTim J. Robbins dprec = 0; 567c5604d0aSTim J. Robbins width = 0; 568c5604d0aSTim J. Robbins prec = -1; 56921ca178eSDavid Schultz gs.grouping = NULL; 570c5604d0aSTim J. Robbins sign = '\0'; 571adfd6b31STim J. Robbins ox[1] = '\0'; 572c5604d0aSTim J. Robbins 573c5604d0aSTim J. Robbins rflag: ch = *fmt++; 574c5604d0aSTim J. Robbins reswitch: switch (ch) { 575c5604d0aSTim J. Robbins case ' ': 576c5604d0aSTim J. Robbins /*- 577c5604d0aSTim J. Robbins * ``If the space and + flags both appear, the space 578c5604d0aSTim J. Robbins * flag will be ignored.'' 579c5604d0aSTim J. Robbins * -- ANSI X3J11 580c5604d0aSTim J. Robbins */ 581c5604d0aSTim J. Robbins if (!sign) 582c5604d0aSTim J. Robbins sign = ' '; 583c5604d0aSTim J. Robbins goto rflag; 584c5604d0aSTim J. Robbins case '#': 585c5604d0aSTim J. Robbins flags |= ALT; 586c5604d0aSTim J. Robbins goto rflag; 587c5604d0aSTim J. Robbins case '*': 588c5604d0aSTim J. Robbins /*- 589c5604d0aSTim J. Robbins * ``A negative field width argument is taken as a 590c5604d0aSTim J. Robbins * - flag followed by a positive field width.'' 591c5604d0aSTim J. Robbins * -- ANSI X3J11 592c5604d0aSTim J. Robbins * They don't exclude field widths read from args. 593c5604d0aSTim J. Robbins */ 594c5604d0aSTim J. Robbins GETASTER (width); 595c5604d0aSTim J. Robbins if (width >= 0) 596c5604d0aSTim J. Robbins goto rflag; 597c5604d0aSTim J. Robbins width = -width; 598c5604d0aSTim J. Robbins /* FALLTHROUGH */ 599c5604d0aSTim J. Robbins case '-': 600c5604d0aSTim J. Robbins flags |= LADJUST; 601c5604d0aSTim J. Robbins goto rflag; 602c5604d0aSTim J. Robbins case '+': 603c5604d0aSTim J. Robbins sign = '+'; 604c5604d0aSTim J. Robbins goto rflag; 605c5604d0aSTim J. Robbins case '\'': 606c5604d0aSTim J. Robbins flags |= GROUPING; 607c5604d0aSTim J. Robbins goto rflag; 608c5604d0aSTim J. Robbins case '.': 609c5604d0aSTim J. Robbins if ((ch = *fmt++) == '*') { 610adfd6b31STim J. Robbins GETASTER (prec); 611c5604d0aSTim J. Robbins goto rflag; 612c5604d0aSTim J. Robbins } 613adfd6b31STim J. Robbins prec = 0; 614c5604d0aSTim J. Robbins while (is_digit(ch)) { 615adfd6b31STim J. Robbins prec = 10 * prec + to_digit(ch); 616c5604d0aSTim J. Robbins ch = *fmt++; 617c5604d0aSTim J. Robbins } 618c5604d0aSTim J. Robbins goto reswitch; 619c5604d0aSTim J. Robbins case '0': 620c5604d0aSTim J. Robbins /*- 621c5604d0aSTim J. Robbins * ``Note that 0 is taken as a flag, not as the 622c5604d0aSTim J. Robbins * beginning of a field width.'' 623c5604d0aSTim J. Robbins * -- ANSI X3J11 624c5604d0aSTim J. Robbins */ 625c5604d0aSTim J. Robbins flags |= ZEROPAD; 626c5604d0aSTim J. Robbins goto rflag; 627c5604d0aSTim J. Robbins case '1': case '2': case '3': case '4': 628c5604d0aSTim J. Robbins case '5': case '6': case '7': case '8': case '9': 629c5604d0aSTim J. Robbins n = 0; 630c5604d0aSTim J. Robbins do { 631c5604d0aSTim J. Robbins n = 10 * n + to_digit(ch); 632c5604d0aSTim J. Robbins ch = *fmt++; 633c5604d0aSTim J. Robbins } while (is_digit(ch)); 634c5604d0aSTim J. Robbins if (ch == '$') { 635c5604d0aSTim J. Robbins nextarg = n; 636c5604d0aSTim J. Robbins if (argtable == NULL) { 637c5604d0aSTim J. Robbins argtable = statargtable; 638e62e5ff9SDavid Schultz if (__find_warguments (fmt0, orgap, 639e62e5ff9SDavid Schultz &argtable)) { 640e62e5ff9SDavid Schultz ret = EOF; 641e62e5ff9SDavid Schultz goto error; 642e62e5ff9SDavid Schultz } 643c5604d0aSTim J. Robbins } 644c5604d0aSTim J. Robbins goto rflag; 645c5604d0aSTim J. Robbins } 646c5604d0aSTim J. Robbins width = n; 647c5604d0aSTim J. Robbins goto reswitch; 648ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT 649c5604d0aSTim J. Robbins case 'L': 650c5604d0aSTim J. Robbins flags |= LONGDBL; 651c5604d0aSTim J. Robbins goto rflag; 652c5604d0aSTim J. Robbins #endif 653c5604d0aSTim J. Robbins case 'h': 654c5604d0aSTim J. Robbins if (flags & SHORTINT) { 655c5604d0aSTim J. Robbins flags &= ~SHORTINT; 656c5604d0aSTim J. Robbins flags |= CHARINT; 657c5604d0aSTim J. Robbins } else 658c5604d0aSTim J. Robbins flags |= SHORTINT; 659c5604d0aSTim J. Robbins goto rflag; 660c5604d0aSTim J. Robbins case 'j': 661c5604d0aSTim J. Robbins flags |= INTMAXT; 662c5604d0aSTim J. Robbins goto rflag; 663c5604d0aSTim J. Robbins case 'l': 664c5604d0aSTim J. Robbins if (flags & LONGINT) { 665c5604d0aSTim J. Robbins flags &= ~LONGINT; 666c5604d0aSTim J. Robbins flags |= LLONGINT; 667c5604d0aSTim J. Robbins } else 668c5604d0aSTim J. Robbins flags |= LONGINT; 669c5604d0aSTim J. Robbins goto rflag; 670c5604d0aSTim J. Robbins case 'q': 671c5604d0aSTim J. Robbins flags |= LLONGINT; /* not necessarily */ 672c5604d0aSTim J. Robbins goto rflag; 673c5604d0aSTim J. Robbins case 't': 674c5604d0aSTim J. Robbins flags |= PTRDIFFT; 675c5604d0aSTim J. Robbins goto rflag; 676c5604d0aSTim J. Robbins case 'z': 677c5604d0aSTim J. Robbins flags |= SIZET; 678c5604d0aSTim J. Robbins goto rflag; 679927ecbf3STim J. Robbins case 'C': 680927ecbf3STim J. Robbins flags |= LONGINT; 681927ecbf3STim J. Robbins /*FALLTHROUGH*/ 682c5604d0aSTim J. Robbins case 'c': 683c5604d0aSTim J. Robbins if (flags & LONGINT) 684c5604d0aSTim J. Robbins *(cp = buf) = (wchar_t)GETARG(wint_t); 685c5604d0aSTim J. Robbins else 686c5604d0aSTim J. Robbins *(cp = buf) = (wchar_t)btowc(GETARG(int)); 687c5604d0aSTim J. Robbins size = 1; 688c5604d0aSTim J. Robbins sign = '\0'; 689c5604d0aSTim J. Robbins break; 690c5604d0aSTim J. Robbins case 'D': 691c5604d0aSTim J. Robbins flags |= LONGINT; 692c5604d0aSTim J. Robbins /*FALLTHROUGH*/ 693c5604d0aSTim J. Robbins case 'd': 694c5604d0aSTim J. Robbins case 'i': 695c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) { 696c5604d0aSTim J. Robbins ujval = SJARG(); 697c5604d0aSTim J. Robbins if ((intmax_t)ujval < 0) { 698c5604d0aSTim J. Robbins ujval = -ujval; 699c5604d0aSTim J. Robbins sign = '-'; 700c5604d0aSTim J. Robbins } 701c5604d0aSTim J. Robbins } else { 702c5604d0aSTim J. Robbins ulval = SARG(); 703c5604d0aSTim J. Robbins if ((long)ulval < 0) { 704c5604d0aSTim J. Robbins ulval = -ulval; 705c5604d0aSTim J. Robbins sign = '-'; 706c5604d0aSTim J. Robbins } 707c5604d0aSTim J. Robbins } 708c5604d0aSTim J. Robbins base = 10; 709c5604d0aSTim J. Robbins goto number; 710ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT 711c5604d0aSTim J. Robbins case 'a': 712c5604d0aSTim J. Robbins case 'A': 713adfd6b31STim J. Robbins if (ch == 'a') { 714adfd6b31STim J. Robbins ox[1] = 'x'; 715adfd6b31STim J. Robbins xdigs = xdigs_lower; 716adfd6b31STim J. Robbins expchar = 'p'; 717adfd6b31STim J. Robbins } else { 718adfd6b31STim J. Robbins ox[1] = 'X'; 719adfd6b31STim J. Robbins xdigs = xdigs_upper; 720adfd6b31STim J. Robbins expchar = 'P'; 721adfd6b31STim J. Robbins } 7227b7e3509SDavid Schultz if (prec >= 0) 7237b7e3509SDavid Schultz prec++; 724adfd6b31STim J. Robbins if (flags & LONGDBL) { 7257b7e3509SDavid Schultz fparg.ldbl = GETARG(long double); 726adfd6b31STim J. Robbins dtoaresult = 727adfd6b31STim J. Robbins __hldtoa(fparg.ldbl, xdigs, prec, 728adfd6b31STim J. Robbins &expt, &signflag, &dtoaend); 729adfd6b31STim J. Robbins } else { 730adfd6b31STim J. Robbins fparg.dbl = GETARG(double); 731adfd6b31STim J. Robbins dtoaresult = 732adfd6b31STim J. Robbins __hdtoa(fparg.dbl, xdigs, prec, 733adfd6b31STim J. Robbins &expt, &signflag, &dtoaend); 734adfd6b31STim J. Robbins } 7357b7e3509SDavid Schultz if (prec < 0) 7367b7e3509SDavid Schultz prec = dtoaend - dtoaresult; 7377b7e3509SDavid Schultz if (expt == INT_MAX) 7387b7e3509SDavid Schultz ox[1] = '\0'; 739adfd6b31STim J. Robbins if (convbuf != NULL) 740adfd6b31STim J. Robbins free(convbuf); 7417b7e3509SDavid Schultz ndig = dtoaend - dtoaresult; 742adfd6b31STim J. Robbins cp = convbuf = __mbsconv(dtoaresult, -1); 743adfd6b31STim J. Robbins freedtoa(dtoaresult); 7447b7e3509SDavid Schultz goto fp_common; 745c5604d0aSTim J. Robbins case 'e': 746c5604d0aSTim J. Robbins case 'E': 747adfd6b31STim J. Robbins expchar = ch; 748adfd6b31STim J. Robbins if (prec < 0) /* account for digit before decpt */ 749adfd6b31STim J. Robbins prec = DEFPREC + 1; 750adfd6b31STim J. Robbins else 751adfd6b31STim J. Robbins prec++; 752adfd6b31STim J. Robbins goto fp_begin; 753c5604d0aSTim J. Robbins case 'f': 754c5604d0aSTim J. Robbins case 'F': 755adfd6b31STim J. Robbins expchar = '\0'; 756c5604d0aSTim J. Robbins goto fp_begin; 757c5604d0aSTim J. Robbins case 'g': 758c5604d0aSTim J. Robbins case 'G': 759adfd6b31STim J. Robbins expchar = ch - ('g' - 'e'); 760c5604d0aSTim J. Robbins if (prec == 0) 761c5604d0aSTim J. Robbins prec = 1; 762adfd6b31STim J. Robbins fp_begin: 763adfd6b31STim J. Robbins if (prec < 0) 764c5604d0aSTim J. Robbins prec = DEFPREC; 765adfd6b31STim J. Robbins if (convbuf != NULL) 766adfd6b31STim J. Robbins free(convbuf); 767adfd6b31STim J. Robbins if (flags & LONGDBL) { 768adfd6b31STim J. Robbins fparg.ldbl = GETARG(long double); 769adfd6b31STim J. Robbins dtoaresult = 770adfd6b31STim J. Robbins __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, 771adfd6b31STim J. Robbins &expt, &signflag, &dtoaend); 772adfd6b31STim J. Robbins } else { 773adfd6b31STim J. Robbins fparg.dbl = GETARG(double); 774adfd6b31STim J. Robbins dtoaresult = 775adfd6b31STim J. Robbins dtoa(fparg.dbl, expchar ? 2 : 3, prec, 776adfd6b31STim J. Robbins &expt, &signflag, &dtoaend); 777adfd6b31STim J. Robbins if (expt == 9999) 778adfd6b31STim J. Robbins expt = INT_MAX; 779c5604d0aSTim J. Robbins } 780adfd6b31STim J. Robbins ndig = dtoaend - dtoaresult; 781adfd6b31STim J. Robbins cp = convbuf = __mbsconv(dtoaresult, -1); 782adfd6b31STim J. Robbins freedtoa(dtoaresult); 7837b7e3509SDavid Schultz fp_common: 784adfd6b31STim J. Robbins if (signflag) 785adfd6b31STim J. Robbins sign = '-'; 786adfd6b31STim J. Robbins if (expt == INT_MAX) { /* inf or nan */ 787adfd6b31STim J. Robbins if (*cp == 'N') { 788adfd6b31STim J. Robbins cp = (ch >= 'a') ? L"nan" : L"NAN"; 789adfd6b31STim J. Robbins sign = '\0'; 790adfd6b31STim J. Robbins } else 791adfd6b31STim J. Robbins cp = (ch >= 'a') ? L"inf" : L"INF"; 792c5604d0aSTim J. Robbins size = 3; 7938da510f8SDavid Schultz flags &= ~ZEROPAD; 794c5604d0aSTim J. Robbins break; 795c5604d0aSTim J. Robbins } 796c5604d0aSTim J. Robbins flags |= FPT; 797c5604d0aSTim J. Robbins if (ch == 'g' || ch == 'G') { 798adfd6b31STim J. Robbins if (expt > -4 && expt <= prec) { 799adfd6b31STim J. Robbins /* Make %[gG] smell like %[fF] */ 800adfd6b31STim J. Robbins expchar = '\0'; 801adfd6b31STim J. Robbins if (flags & ALT) 802adfd6b31STim J. Robbins prec -= expt; 803c5604d0aSTim J. Robbins else 804adfd6b31STim J. Robbins prec = ndig - expt; 805adfd6b31STim J. Robbins if (prec < 0) 806adfd6b31STim J. Robbins prec = 0; 8071f2a0cdfSDavid Schultz } else { 8081f2a0cdfSDavid Schultz /* 8091f2a0cdfSDavid Schultz * Make %[gG] smell like %[eE], but 8101f2a0cdfSDavid Schultz * trim trailing zeroes if no # flag. 8111f2a0cdfSDavid Schultz */ 8121f2a0cdfSDavid Schultz if (!(flags & ALT)) 8131f2a0cdfSDavid Schultz prec = ndig; 814c5604d0aSTim J. Robbins } 815adfd6b31STim J. Robbins } 816adfd6b31STim J. Robbins if (expchar) { 817adfd6b31STim J. Robbins expsize = exponent(expstr, expt - 1, expchar); 818adfd6b31STim J. Robbins size = expsize + prec; 819adfd6b31STim J. Robbins if (prec > 1 || flags & ALT) 820c5604d0aSTim J. Robbins ++size; 821adfd6b31STim J. Robbins } else { 822d73c448bSTim J. Robbins /* space for digits before decimal point */ 823d73c448bSTim J. Robbins if (expt > 0) 824c5604d0aSTim J. Robbins size = expt; 825d73c448bSTim J. Robbins else /* "0" */ 826d73c448bSTim J. Robbins size = 1; 827d73c448bSTim J. Robbins /* space for decimal pt and following digits */ 828c5604d0aSTim J. Robbins if (prec || flags & ALT) 829c5604d0aSTim J. Robbins size += prec + 1; 83021ca178eSDavid Schultz if ((flags & GROUPING) && expt > 0) 831*3c87aa1dSDavid Chisnall size += grouping_init(&gs, expt, locale); 832adfd6b31STim J. Robbins } 833c5604d0aSTim J. Robbins break; 834ce2551adSDavid Schultz #endif /* !NO_FLOATING_POINT */ 835c5604d0aSTim J. Robbins case 'n': 836c5604d0aSTim J. Robbins /* 837c5604d0aSTim J. Robbins * Assignment-like behavior is specified if the 838c5604d0aSTim J. Robbins * value overflows or is otherwise unrepresentable. 839c5604d0aSTim J. Robbins * C99 says to use `signed char' for %hhn conversions. 840c5604d0aSTim J. Robbins */ 841c5604d0aSTim J. Robbins if (flags & LLONGINT) 842c5604d0aSTim J. Robbins *GETARG(long long *) = ret; 843c5604d0aSTim J. Robbins else if (flags & SIZET) 844c5604d0aSTim J. Robbins *GETARG(ssize_t *) = (ssize_t)ret; 845c5604d0aSTim J. Robbins else if (flags & PTRDIFFT) 846c5604d0aSTim J. Robbins *GETARG(ptrdiff_t *) = ret; 847c5604d0aSTim J. Robbins else if (flags & INTMAXT) 848c5604d0aSTim J. Robbins *GETARG(intmax_t *) = ret; 849c5604d0aSTim J. Robbins else if (flags & LONGINT) 850c5604d0aSTim J. Robbins *GETARG(long *) = ret; 851c5604d0aSTim J. Robbins else if (flags & SHORTINT) 852c5604d0aSTim J. Robbins *GETARG(short *) = ret; 853c5604d0aSTim J. Robbins else if (flags & CHARINT) 854c5604d0aSTim J. Robbins *GETARG(signed char *) = ret; 855c5604d0aSTim J. Robbins else 856c5604d0aSTim J. Robbins *GETARG(int *) = ret; 857c5604d0aSTim J. Robbins continue; /* no output */ 858c5604d0aSTim J. Robbins case 'O': 859c5604d0aSTim J. Robbins flags |= LONGINT; 860c5604d0aSTim J. Robbins /*FALLTHROUGH*/ 861c5604d0aSTim J. Robbins case 'o': 862c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) 863c5604d0aSTim J. Robbins ujval = UJARG(); 864c5604d0aSTim J. Robbins else 865c5604d0aSTim J. Robbins ulval = UARG(); 866c5604d0aSTim J. Robbins base = 8; 867c5604d0aSTim J. Robbins goto nosign; 868c5604d0aSTim J. Robbins case 'p': 869c5604d0aSTim J. Robbins /*- 870c5604d0aSTim J. Robbins * ``The argument shall be a pointer to void. The 871c5604d0aSTim J. Robbins * value of the pointer is converted to a sequence 872c5604d0aSTim J. Robbins * of printable characters, in an implementation- 873c5604d0aSTim J. Robbins * defined manner.'' 874c5604d0aSTim J. Robbins * -- ANSI X3J11 875c5604d0aSTim J. Robbins */ 876c5604d0aSTim J. Robbins ujval = (uintmax_t)(uintptr_t)GETARG(void *); 877c5604d0aSTim J. Robbins base = 16; 878adfd6b31STim J. Robbins xdigs = xdigs_lower; 879adfd6b31STim J. Robbins flags = flags | INTMAXT; 880adfd6b31STim J. Robbins ox[1] = 'x'; 881c5604d0aSTim J. Robbins goto nosign; 882927ecbf3STim J. Robbins case 'S': 883927ecbf3STim J. Robbins flags |= LONGINT; 884927ecbf3STim J. Robbins /*FALLTHROUGH*/ 885c5604d0aSTim J. Robbins case 's': 886c5604d0aSTim J. Robbins if (flags & LONGINT) { 887c5604d0aSTim J. Robbins if ((cp = GETARG(wchar_t *)) == NULL) 888c5604d0aSTim J. Robbins cp = L"(null)"; 889c5604d0aSTim J. Robbins } else { 890c5604d0aSTim J. Robbins char *mbp; 891c5604d0aSTim J. Robbins 892c5604d0aSTim J. Robbins if (convbuf != NULL) 893c5604d0aSTim J. Robbins free(convbuf); 894c5604d0aSTim J. Robbins if ((mbp = GETARG(char *)) == NULL) 895c5604d0aSTim J. Robbins cp = L"(null)"; 896c5604d0aSTim J. Robbins else { 897c5604d0aSTim J. Robbins convbuf = __mbsconv(mbp, prec); 8986180233fSTim J. Robbins if (convbuf == NULL) { 8996180233fSTim J. Robbins fp->_flags |= __SERR; 900c5604d0aSTim J. Robbins goto error; 9016180233fSTim J. Robbins } 902c5604d0aSTim J. Robbins cp = convbuf; 903c5604d0aSTim J. Robbins } 904c5604d0aSTim J. Robbins } 905353ce11cSDavid Schultz size = (prec >= 0) ? wcsnlen(cp, prec) : wcslen(cp); 906c5604d0aSTim J. Robbins sign = '\0'; 907c5604d0aSTim J. Robbins break; 908c5604d0aSTim J. Robbins case 'U': 909c5604d0aSTim J. Robbins flags |= LONGINT; 910c5604d0aSTim J. Robbins /*FALLTHROUGH*/ 911c5604d0aSTim J. Robbins case 'u': 912c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) 913c5604d0aSTim J. Robbins ujval = UJARG(); 914c5604d0aSTim J. Robbins else 915c5604d0aSTim J. Robbins ulval = UARG(); 916c5604d0aSTim J. Robbins base = 10; 917c5604d0aSTim J. Robbins goto nosign; 918c5604d0aSTim J. Robbins case 'X': 919adfd6b31STim J. Robbins xdigs = xdigs_upper; 920c5604d0aSTim J. Robbins goto hex; 921c5604d0aSTim J. Robbins case 'x': 922adfd6b31STim J. Robbins xdigs = xdigs_lower; 923c5604d0aSTim J. Robbins hex: 924c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) 925c5604d0aSTim J. Robbins ujval = UJARG(); 926c5604d0aSTim J. Robbins else 927c5604d0aSTim J. Robbins ulval = UARG(); 928c5604d0aSTim J. Robbins base = 16; 929c5604d0aSTim J. Robbins /* leading 0x/X only if non-zero */ 930c5604d0aSTim J. Robbins if (flags & ALT && 931c5604d0aSTim J. Robbins (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) 932adfd6b31STim J. Robbins ox[1] = ch; 933c5604d0aSTim J. Robbins 934c5604d0aSTim J. Robbins flags &= ~GROUPING; 935c5604d0aSTim J. Robbins /* unsigned conversions */ 936c5604d0aSTim J. Robbins nosign: sign = '\0'; 937c5604d0aSTim J. Robbins /*- 938c5604d0aSTim J. Robbins * ``... diouXx conversions ... if a precision is 939c5604d0aSTim J. Robbins * specified, the 0 flag will be ignored.'' 940c5604d0aSTim J. Robbins * -- ANSI X3J11 941c5604d0aSTim J. Robbins */ 942c5604d0aSTim J. Robbins number: if ((dprec = prec) >= 0) 943c5604d0aSTim J. Robbins flags &= ~ZEROPAD; 944c5604d0aSTim J. Robbins 945c5604d0aSTim J. Robbins /*- 946c5604d0aSTim J. Robbins * ``The result of converting a zero value with an 947c5604d0aSTim J. Robbins * explicit precision of zero is no characters.'' 948c5604d0aSTim J. Robbins * -- ANSI X3J11 9491be5319aSDavid Schultz * 9501be5319aSDavid Schultz * ``The C Standard is clear enough as is. The call 9511be5319aSDavid Schultz * printf("%#.0o", 0) should print 0.'' 9521be5319aSDavid Schultz * -- Defect Report #151 953c5604d0aSTim J. Robbins */ 954c5604d0aSTim J. Robbins cp = buf + BUF; 955c5604d0aSTim J. Robbins if (flags & INTMAX_SIZE) { 9561be5319aSDavid Schultz if (ujval != 0 || prec != 0 || 9571be5319aSDavid Schultz (flags & ALT && base == 8)) 958c5604d0aSTim J. Robbins cp = __ujtoa(ujval, cp, base, 95921ca178eSDavid Schultz flags & ALT, xdigs); 960c5604d0aSTim J. Robbins } else { 9611be5319aSDavid Schultz if (ulval != 0 || prec != 0 || 9621be5319aSDavid Schultz (flags & ALT && base == 8)) 963c5604d0aSTim J. Robbins cp = __ultoa(ulval, cp, base, 96421ca178eSDavid Schultz flags & ALT, xdigs); 965c5604d0aSTim J. Robbins } 966c5604d0aSTim J. Robbins size = buf + BUF - cp; 96738cac8f8SDavid Schultz if (size > BUF) /* should never happen */ 96838cac8f8SDavid Schultz abort(); 96921ca178eSDavid Schultz if ((flags & GROUPING) && size != 0) 970*3c87aa1dSDavid Chisnall size += grouping_init(&gs, size, locale); 971c5604d0aSTim J. Robbins break; 972c5604d0aSTim J. Robbins default: /* "%?" prints ?, unless ? is NUL */ 973c5604d0aSTim J. Robbins if (ch == '\0') 974c5604d0aSTim J. Robbins goto done; 975c5604d0aSTim J. Robbins /* pretend it was %c with argument ch */ 976c5604d0aSTim J. Robbins cp = buf; 977c5604d0aSTim J. Robbins *cp = ch; 978c5604d0aSTim J. Robbins size = 1; 979c5604d0aSTim J. Robbins sign = '\0'; 980c5604d0aSTim J. Robbins break; 981c5604d0aSTim J. Robbins } 982c5604d0aSTim J. Robbins 983c5604d0aSTim J. Robbins /* 984c5604d0aSTim J. Robbins * All reasonable formats wind up here. At this point, `cp' 985c5604d0aSTim J. Robbins * points to a string which (if not flags&LADJUST) should be 986c5604d0aSTim J. Robbins * padded out to `width' places. If flags&ZEROPAD, it should 987c5604d0aSTim J. Robbins * first be prefixed by any sign or other prefix; otherwise, 988c5604d0aSTim J. Robbins * it should be blank padded before the prefix is emitted. 989c5604d0aSTim J. Robbins * After any left-hand padding and prefixing, emit zeroes 990c5604d0aSTim J. Robbins * required by a decimal [diouxX] precision, then print the 991c5604d0aSTim J. Robbins * string proper, then emit zeroes required by any leftover 992c5604d0aSTim J. Robbins * floating precision; finally, if LADJUST, pad with blanks. 993c5604d0aSTim J. Robbins * 994c5604d0aSTim J. Robbins * Compute actual size, so we know how much to pad. 995c5604d0aSTim J. Robbins * size excludes decimal prec; realsz includes it. 996c5604d0aSTim J. Robbins */ 997c5604d0aSTim J. Robbins realsz = dprec > size ? dprec : size; 998c5604d0aSTim J. Robbins if (sign) 999c5604d0aSTim J. Robbins realsz++; 10007b7e3509SDavid Schultz if (ox[1]) 1001c5604d0aSTim J. Robbins realsz += 2; 1002c5604d0aSTim J. Robbins 1003c5604d0aSTim J. Robbins prsize = width > realsz ? width : realsz; 1004c5604d0aSTim J. Robbins if ((unsigned)ret + prsize > INT_MAX) { 1005c5604d0aSTim J. Robbins ret = EOF; 1006c5604d0aSTim J. Robbins goto error; 1007c5604d0aSTim J. Robbins } 1008c5604d0aSTim J. Robbins 1009c5604d0aSTim J. Robbins /* right-adjusting blank padding */ 1010c5604d0aSTim J. Robbins if ((flags & (LADJUST|ZEROPAD)) == 0) 1011c5604d0aSTim J. Robbins PAD(width - realsz, blanks); 1012c5604d0aSTim J. Robbins 1013c5604d0aSTim J. Robbins /* prefix */ 10147b7e3509SDavid Schultz if (sign) 1015c5604d0aSTim J. Robbins PRINT(&sign, 1); 10167b7e3509SDavid Schultz 10177b7e3509SDavid Schultz if (ox[1]) { /* ox[1] is either x, X, or \0 */ 1018c5604d0aSTim J. Robbins ox[0] = '0'; 1019c5604d0aSTim J. Robbins PRINT(ox, 2); 1020c5604d0aSTim J. Robbins } 1021c5604d0aSTim J. Robbins 1022c5604d0aSTim J. Robbins /* right-adjusting zero padding */ 1023c5604d0aSTim J. Robbins if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 1024c5604d0aSTim J. Robbins PAD(width - realsz, zeroes); 1025c5604d0aSTim J. Robbins 1026c5604d0aSTim J. Robbins /* the string or number proper */ 1027ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT 1028c5604d0aSTim J. Robbins if ((flags & FPT) == 0) { 102921ca178eSDavid Schultz #endif 103021ca178eSDavid Schultz /* leading zeroes from decimal precision */ 103121ca178eSDavid Schultz PAD(dprec - size, zeroes); 103221ca178eSDavid Schultz if (gs.grouping) { 1033*3c87aa1dSDavid Chisnall if (grouping_print(&gs, &io, cp, buf+BUF, locale) < 0) 103421ca178eSDavid Schultz goto error; 103521ca178eSDavid Schultz } else { 1036c5604d0aSTim J. Robbins PRINT(cp, size); 103721ca178eSDavid Schultz } 103821ca178eSDavid Schultz #ifndef NO_FLOATING_POINT 1039c5604d0aSTim J. Robbins } else { /* glue together f_p fragments */ 1040adfd6b31STim J. Robbins if (!expchar) { /* %[fF] or sufficiently short %[gG] */ 1041adfd6b31STim J. Robbins if (expt <= 0) { 1042d73c448bSTim J. Robbins PRINT(zeroes, 1); 10435004a238SDavid Schultz if (prec || flags & ALT) 10445004a238SDavid Schultz PRINT(&decimal_point, 1); 1045c5604d0aSTim J. Robbins PAD(-expt, zeroes); 1046adfd6b31STim J. Robbins /* already handled initial 0's */ 1047adfd6b31STim J. Robbins prec += expt; 1048c5604d0aSTim J. Robbins } else { 104921ca178eSDavid Schultz if (gs.grouping) { 105021ca178eSDavid Schultz n = grouping_print(&gs, &io, 1051*3c87aa1dSDavid Chisnall cp, convbuf + ndig, locale); 105221ca178eSDavid Schultz if (n < 0) 105321ca178eSDavid Schultz goto error; 105421ca178eSDavid Schultz cp += n; 105521ca178eSDavid Schultz } else { 105621ca178eSDavid Schultz PRINTANDPAD(cp, convbuf + ndig, 105721ca178eSDavid Schultz expt, zeroes); 105821ca178eSDavid Schultz cp += expt; 1059adfd6b31STim J. Robbins } 10605004a238SDavid Schultz if (prec || flags & ALT) 10615004a238SDavid Schultz PRINT(&decimal_point, 1); 1062adfd6b31STim J. Robbins } 1063adfd6b31STim J. Robbins PRINTANDPAD(cp, convbuf + ndig, prec, zeroes); 1064adfd6b31STim J. Robbins } else { /* %[eE] or sufficiently long %[gG] */ 1065adfd6b31STim J. Robbins if (prec > 1 || flags & ALT) { 1066adfd6b31STim J. Robbins buf[0] = *cp++; 10675004a238SDavid Schultz buf[1] = decimal_point; 1068adfd6b31STim J. Robbins PRINT(buf, 2); 1069c5604d0aSTim J. Robbins PRINT(cp, ndig-1); 1070adfd6b31STim J. Robbins PAD(prec - ndig, zeroes); 1071c5604d0aSTim J. Robbins } else /* XeYYY */ 1072c5604d0aSTim J. Robbins PRINT(cp, 1); 1073c5604d0aSTim J. Robbins PRINT(expstr, expsize); 1074c5604d0aSTim J. Robbins } 1075c5604d0aSTim J. Robbins } 1076c5604d0aSTim J. Robbins #endif 1077c5604d0aSTim J. Robbins /* left-adjusting padding (always blank) */ 1078c5604d0aSTim J. Robbins if (flags & LADJUST) 1079c5604d0aSTim J. Robbins PAD(width - realsz, blanks); 1080c5604d0aSTim J. Robbins 1081c5604d0aSTim J. Robbins /* finally, adjust ret */ 1082c5604d0aSTim J. Robbins ret += prsize; 1083814d1bc9SDavid Schultz 1084814d1bc9SDavid Schultz FLUSH(); /* copy out the I/O vectors */ 1085c5604d0aSTim J. Robbins } 1086c5604d0aSTim J. Robbins done: 1087814d1bc9SDavid Schultz FLUSH(); 1088c5604d0aSTim J. Robbins error: 1089096ad104SDag-Erling Smørgrav va_end(orgap); 1090c5604d0aSTim J. Robbins if (convbuf != NULL) 1091c5604d0aSTim J. Robbins free(convbuf); 1092c5604d0aSTim J. Robbins if (__sferror(fp)) 1093c5604d0aSTim J. Robbins ret = EOF; 1094c5604d0aSTim J. Robbins if ((argtable != NULL) && (argtable != statargtable)) 1095c5604d0aSTim J. Robbins free (argtable); 1096c5604d0aSTim J. Robbins return (ret); 1097c5604d0aSTim J. Robbins /* NOTREACHED */ 1098c5604d0aSTim J. Robbins } 1099