1103739Stjr/*- 2103739Stjr * Copyright (c) 1990, 1993 3103739Stjr * The Regents of the University of California. All rights reserved. 4103739Stjr * 5103739Stjr * This code is derived from software contributed to Berkeley by 6103739Stjr * Chris Torek. 7103739Stjr * 8227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation 9227753Stheraven * All rights reserved. 10227753Stheraven * Portions of this software were developed by David Chisnall 11227753Stheraven * under sponsorship from the FreeBSD Foundation. 12227753Stheraven * 13103739Stjr * Redistribution and use in source and binary forms, with or without 14103739Stjr * modification, are permitted provided that the following conditions 15103739Stjr * are met: 16103739Stjr * 1. Redistributions of source code must retain the above copyright 17103739Stjr * notice, this list of conditions and the following disclaimer. 18103739Stjr * 2. Redistributions in binary form must reproduce the above copyright 19103739Stjr * notice, this list of conditions and the following disclaimer in the 20103739Stjr * documentation and/or other materials provided with the distribution. 21249808Semaste * 3. Neither the name of the University nor the names of its contributors 22103739Stjr * may be used to endorse or promote products derived from this software 23103739Stjr * without specific prior written permission. 24103739Stjr * 25103739Stjr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26103739Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27103739Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28103739Stjr * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29103739Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30103739Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31103739Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32103739Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33103739Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34103739Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35103739Stjr * SUCH DAMAGE. 36103739Stjr */ 37103739Stjr 38103739Stjr#if 0 39103739Stjr#if defined(LIBC_SCCS) && !defined(lint) 40103739Stjrstatic char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; 41103739Stjr#endif /* LIBC_SCCS and not lint */ 42103739Stjr#endif 43128843Sobrien#include <sys/cdefs.h> 44103739Stjr__FBSDID("$FreeBSD: stable/10/lib/libc/stdio/vfwprintf.c 321074 2017-07-17 14:09:34Z kib $"); 45103739Stjr 46103739Stjr/* 47103739Stjr * Actual wprintf innards. 48103739Stjr * 49103739Stjr * Avoid making gratuitous changes to this source file; it should be kept 50103739Stjr * as close as possible to vfprintf.c for ease of maintenance. 51103739Stjr */ 52103739Stjr 53103739Stjr#include "namespace.h" 54103739Stjr#include <sys/types.h> 55103739Stjr 56103739Stjr#include <ctype.h> 57234531Sdas#include <errno.h> 58103739Stjr#include <limits.h> 59103739Stjr#include <locale.h> 60103739Stjr#include <stdarg.h> 61103739Stjr#include <stddef.h> 62103739Stjr#include <stdint.h> 63103739Stjr#include <stdio.h> 64103739Stjr#include <stdlib.h> 65103739Stjr#include <string.h> 66103739Stjr#include <wchar.h> 67103739Stjr#include <wctype.h> 68103739Stjr#include "un-namespace.h" 69103739Stjr 70103739Stjr#include "libc_private.h" 71103739Stjr#include "local.h" 72103739Stjr#include "fvwrite.h" 73180104Sdas#include "printflocal.h" 74227753Stheraven#include "xlocale_private.h" 75103739Stjr 76227753Stheravenstatic int __sprint(FILE *, struct __suio *, locale_t); 77227753Stheravenstatic int __sbprintf(FILE *, locale_t, const wchar_t *, va_list) __noinline; 78227753Stheravenstatic wint_t __xfputwc(wchar_t, FILE *, locale_t); 79103739Stjrstatic wchar_t *__mbsconv(char *, int); 80103739Stjr 81187277Sdas#define CHAR wchar_t 82187277Sdas#include "printfcommon.h" 83187277Sdas 84187582Sdasstruct grouping_state { 85187582Sdas wchar_t thousands_sep; /* locale-specific thousands separator */ 86187582Sdas const char *grouping; /* locale-specific numeric grouping rules */ 87187582Sdas int lead; /* sig figs before decimal or group sep */ 88187582Sdas int nseps; /* number of group separators with ' */ 89187582Sdas int nrepeats; /* number of repeats of the last group */ 90187582Sdas}; 91187582Sdas 92187421Sdasstatic const mbstate_t initial_mbs; 93187421Sdas 94187421Sdasstatic inline wchar_t 95227753Stheravenget_decpt(locale_t locale) 96187421Sdas{ 97187421Sdas mbstate_t mbs; 98187421Sdas wchar_t decpt; 99187421Sdas int nconv; 100187421Sdas 101187421Sdas mbs = initial_mbs; 102227753Stheraven nconv = mbrtowc(&decpt, localeconv_l(locale)->decimal_point, MB_CUR_MAX, &mbs); 103187421Sdas if (nconv == (size_t)-1 || nconv == (size_t)-2) 104187421Sdas decpt = '.'; /* failsafe */ 105187421Sdas return (decpt); 106187421Sdas} 107187421Sdas 108187582Sdasstatic inline wchar_t 109227753Stheravenget_thousep(locale_t locale) 110187582Sdas{ 111187582Sdas mbstate_t mbs; 112187582Sdas wchar_t thousep; 113187582Sdas int nconv; 114187582Sdas 115187582Sdas mbs = initial_mbs; 116227753Stheraven nconv = mbrtowc(&thousep, localeconv_l(locale)->thousands_sep, 117187582Sdas MB_CUR_MAX, &mbs); 118187582Sdas if (nconv == (size_t)-1 || nconv == (size_t)-2) 119187582Sdas thousep = '\0'; /* failsafe */ 120187582Sdas return (thousep); 121187582Sdas} 122187582Sdas 123103739Stjr/* 124187582Sdas * Initialize the thousands' grouping state in preparation to print a 125187582Sdas * number with ndigits digits. This routine returns the total number 126187582Sdas * of wide characters that will be printed. 127187582Sdas */ 128187582Sdasstatic int 129227753Stheravengrouping_init(struct grouping_state *gs, int ndigits, locale_t locale) 130187582Sdas{ 131187582Sdas 132227753Stheraven gs->grouping = localeconv_l(locale)->grouping; 133227753Stheraven gs->thousands_sep = get_thousep(locale); 134187582Sdas 135187582Sdas gs->nseps = gs->nrepeats = 0; 136187582Sdas gs->lead = ndigits; 137187582Sdas while (*gs->grouping != CHAR_MAX) { 138187582Sdas if (gs->lead <= *gs->grouping) 139187582Sdas break; 140187582Sdas gs->lead -= *gs->grouping; 141187582Sdas if (*(gs->grouping+1)) { 142187582Sdas gs->nseps++; 143187582Sdas gs->grouping++; 144187582Sdas } else 145187582Sdas gs->nrepeats++; 146187582Sdas } 147187582Sdas return (gs->nseps + gs->nrepeats); 148187582Sdas} 149187582Sdas 150187582Sdas/* 151187582Sdas * Print a number with thousands' separators. 152187582Sdas */ 153187582Sdasstatic int 154187582Sdasgrouping_print(struct grouping_state *gs, struct io_state *iop, 155227753Stheraven const CHAR *cp, const CHAR *ep, locale_t locale) 156187582Sdas{ 157187582Sdas const CHAR *cp0 = cp; 158187582Sdas 159227753Stheraven if (io_printandpad(iop, cp, ep, gs->lead, zeroes, locale)) 160187582Sdas return (-1); 161187582Sdas cp += gs->lead; 162187582Sdas while (gs->nseps > 0 || gs->nrepeats > 0) { 163187582Sdas if (gs->nrepeats > 0) 164187582Sdas gs->nrepeats--; 165187582Sdas else { 166187582Sdas gs->grouping--; 167187582Sdas gs->nseps--; 168187582Sdas } 169227753Stheraven if (io_print(iop, &gs->thousands_sep, 1, locale)) 170187582Sdas return (-1); 171227753Stheraven if (io_printandpad(iop, cp, ep, *gs->grouping, zeroes, locale)) 172187582Sdas return (-1); 173187582Sdas cp += *gs->grouping; 174187582Sdas } 175187582Sdas if (cp > ep) 176187582Sdas cp = ep; 177187582Sdas return (cp - cp0); 178187582Sdas} 179187582Sdas 180187582Sdas 181187582Sdas/* 182187277Sdas * Flush out all the vectors defined by the given uio, 183187277Sdas * then reset it so that it can be reused. 184187277Sdas * 185187277Sdas * XXX The fact that we do this a character at a time and convert to a 186187277Sdas * multibyte character sequence even if the destination is a wide 187187277Sdas * string eclipses the benefits of buffering. 188187277Sdas */ 189187277Sdasstatic int 190227753Stheraven__sprint(FILE *fp, struct __suio *uio, locale_t locale) 191187277Sdas{ 192187277Sdas struct __siov *iov; 193187277Sdas wchar_t *p; 194187277Sdas int i, len; 195187277Sdas 196187277Sdas iov = uio->uio_iov; 197187277Sdas for (; uio->uio_resid != 0; uio->uio_resid -= len, iov++) { 198187277Sdas p = (wchar_t *)iov->iov_base; 199187277Sdas len = iov->iov_len; 200187277Sdas for (i = 0; i < len; i++) { 201227753Stheraven if (__xfputwc(p[i], fp, locale) == WEOF) 202187277Sdas return (-1); 203187277Sdas } 204187277Sdas } 205187277Sdas uio->uio_iovcnt = 0; 206187277Sdas return (0); 207187277Sdas} 208187277Sdas 209187277Sdas/* 210103739Stjr * Helper function for `fprintf to unbuffered unix file': creates a 211103739Stjr * temporary buffer. We only work on write-only files; this avoids 212103739Stjr * worries about ungetc buffers and so forth. 213103739Stjr */ 214103739Stjrstatic int 215227753Stheraven__sbprintf(FILE *fp, locale_t locale, const wchar_t *fmt, va_list ap) 216103739Stjr{ 217103739Stjr int ret; 218103739Stjr FILE fake; 219103739Stjr unsigned char buf[BUFSIZ]; 220103739Stjr 221187369Sdas /* XXX This is probably not needed. */ 222187369Sdas if (prepwrite(fp) != 0) 223187369Sdas return (EOF); 224187369Sdas 225103739Stjr /* copy the important variables */ 226103739Stjr fake._flags = fp->_flags & ~__SNBF; 227103739Stjr fake._file = fp->_file; 228103739Stjr fake._cookie = fp->_cookie; 229103739Stjr fake._write = fp->_write; 230178287Sjhb fake._orientation = fp->_orientation; 231178287Sjhb fake._mbstate = fp->_mbstate; 232103739Stjr 233103739Stjr /* set up the buffer */ 234103739Stjr fake._bf._base = fake._p = buf; 235103739Stjr fake._bf._size = fake._w = sizeof(buf); 236103739Stjr fake._lbfsize = 0; /* not actually used, but Just In Case */ 237103739Stjr 238103739Stjr /* do the work, then copy any error status */ 239227753Stheraven ret = __vfwprintf(&fake, locale, fmt, ap); 240103739Stjr if (ret >= 0 && __fflush(&fake)) 241103739Stjr ret = WEOF; 242103739Stjr if (fake._flags & __SERR) 243103739Stjr fp->_flags |= __SERR; 244103739Stjr return (ret); 245103739Stjr} 246103739Stjr 247103739Stjr/* 248122547Stjr * Like __fputwc, but handles fake string (__SSTR) files properly. 249122547Stjr * File must already be locked. 250122547Stjr */ 251122547Stjrstatic wint_t 252227753Stheraven__xfputwc(wchar_t wc, FILE *fp, locale_t locale) 253122547Stjr{ 254128002Stjr mbstate_t mbs; 255122547Stjr char buf[MB_LEN_MAX]; 256122547Stjr struct __suio uio; 257122547Stjr struct __siov iov; 258124174Snectar size_t len; 259122547Stjr 260122547Stjr if ((fp->_flags & __SSTR) == 0) 261227753Stheraven return (__fputwc(wc, fp, locale)); 262122547Stjr 263187421Sdas mbs = initial_mbs; 264128002Stjr if ((len = wcrtomb(buf, wc, &mbs)) == (size_t)-1) { 265122547Stjr fp->_flags |= __SERR; 266122547Stjr return (WEOF); 267122547Stjr } 268122547Stjr uio.uio_iov = &iov; 269122547Stjr uio.uio_resid = len; 270122547Stjr uio.uio_iovcnt = 1; 271122547Stjr iov.iov_base = buf; 272122547Stjr iov.iov_len = len; 273122547Stjr return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF); 274122547Stjr} 275122547Stjr 276122547Stjr/* 277103739Stjr * Convert a multibyte character string argument for the %s format to a wide 278103739Stjr * string representation. ``prec'' specifies the maximum number of bytes 279103739Stjr * to output. If ``prec'' is greater than or equal to zero, we can't assume 280103739Stjr * that the multibyte char. string ends in a null character. 281103739Stjr */ 282103739Stjrstatic wchar_t * 283103739Stjr__mbsconv(char *mbsarg, int prec) 284103739Stjr{ 285128002Stjr mbstate_t mbs; 286103739Stjr wchar_t *convbuf, *wcp; 287103739Stjr const char *p; 288103739Stjr size_t insize, nchars, nconv; 289103739Stjr 290113199Stjr if (mbsarg == NULL) 291113199Stjr return (NULL); 292113199Stjr 293103739Stjr /* 294103739Stjr * Supplied argument is a multibyte string; convert it to wide 295103739Stjr * characters first. 296103739Stjr */ 297103739Stjr if (prec >= 0) { 298103739Stjr /* 299103739Stjr * String is not guaranteed to be NUL-terminated. Find the 300103739Stjr * number of characters to print. 301103739Stjr */ 302103739Stjr p = mbsarg; 303199782Swollman insize = nchars = nconv = 0; 304187421Sdas mbs = initial_mbs; 305103739Stjr while (nchars != (size_t)prec) { 306128002Stjr nconv = mbrlen(p, MB_CUR_MAX, &mbs); 307103739Stjr if (nconv == 0 || nconv == (size_t)-1 || 308103739Stjr nconv == (size_t)-2) 309103739Stjr break; 310103739Stjr p += nconv; 311103739Stjr nchars++; 312103739Stjr insize += nconv; 313103739Stjr } 314103739Stjr if (nconv == (size_t)-1 || nconv == (size_t)-2) 315103739Stjr return (NULL); 316181281Scperciva } else { 317103739Stjr insize = strlen(mbsarg); 318181281Scperciva nconv = 0; 319181281Scperciva } 320103739Stjr 321103739Stjr /* 322103739Stjr * Allocate buffer for the result and perform the conversion, 323103739Stjr * converting at most `size' bytes of the input multibyte string to 324103739Stjr * wide characters for printing. 325103739Stjr */ 326103739Stjr convbuf = malloc((insize + 1) * sizeof(*convbuf)); 327103739Stjr if (convbuf == NULL) 328103739Stjr return (NULL); 329103739Stjr wcp = convbuf; 330103739Stjr p = mbsarg; 331187421Sdas mbs = initial_mbs; 332103739Stjr while (insize != 0) { 333128002Stjr nconv = mbrtowc(wcp, p, insize, &mbs); 334103739Stjr if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) 335103739Stjr break; 336103739Stjr wcp++; 337103739Stjr p += nconv; 338103739Stjr insize -= nconv; 339103739Stjr } 340103739Stjr if (nconv == (size_t)-1 || nconv == (size_t)-2) { 341103739Stjr free(convbuf); 342103739Stjr return (NULL); 343103739Stjr } 344103739Stjr *wcp = L'\0'; 345103739Stjr 346103739Stjr return (convbuf); 347103739Stjr} 348103739Stjr 349103739Stjr/* 350103739Stjr * MT-safe version 351103739Stjr */ 352103739Stjrint 353227753Stheravenvfwprintf_l(FILE * __restrict fp, locale_t locale, 354227753Stheraven const wchar_t * __restrict fmt0, va_list ap) 355103739Stjr 356103739Stjr{ 357103739Stjr int ret; 358227753Stheraven FIX_LOCALE(locale); 359321074Skib FLOCKFILE_CANCELSAFE(fp); 360187369Sdas /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 361187369Sdas if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 362187369Sdas fp->_file >= 0) 363227753Stheraven ret = __sbprintf(fp, locale, fmt0, ap); 364187369Sdas else 365227753Stheraven ret = __vfwprintf(fp, locale, fmt0, ap); 366321074Skib FUNLOCKFILE_CANCELSAFE(); 367103739Stjr return (ret); 368103739Stjr} 369227753Stheravenint 370227753Stheravenvfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, va_list ap) 371227753Stheraven{ 372227753Stheraven return vfwprintf_l(fp, __get_locale(), fmt0, ap); 373227753Stheraven} 374103739Stjr 375113142Sdas/* 376113142Sdas * The size of the buffer we use as scratch space for integer 377187582Sdas * conversions, among other things. We need enough space to 378187582Sdas * write a uintmax_t in octal (plus one byte). 379113142Sdas */ 380187582Sdas#if UINTMAX_MAX <= UINT64_MAX 381187582Sdas#define BUF 32 382187582Sdas#else 383187582Sdas#error "BUF must be large enough to format a uintmax_t" 384187582Sdas#endif 385103739Stjr 386103739Stjr/* 387103739Stjr * Non-MT-safe version 388103739Stjr */ 389103739Stjrint 390227753Stheraven__vfwprintf(FILE *fp, locale_t locale, const wchar_t *fmt0, va_list ap) 391103739Stjr{ 392103739Stjr wchar_t *fmt; /* format string */ 393103739Stjr wchar_t ch; /* character from fmt */ 394187277Sdas int n, n2; /* handy integer (short term usage) */ 395103739Stjr wchar_t *cp; /* handy char pointer (short term usage) */ 396103739Stjr int flags; /* flags as above */ 397103739Stjr int ret; /* return value accumulator */ 398103739Stjr int width; /* width from format (%8d), or 0 */ 399113199Stjr int prec; /* precision from format; <0 for N/A */ 400103739Stjr wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */ 401187582Sdas struct grouping_state gs; /* thousands' grouping info */ 402128821Sdas#ifndef NO_FLOATING_POINT 403113199Stjr /* 404113199Stjr * We can decompose the printed representation of floating 405113199Stjr * point numbers into several parts, some of which may be empty: 406113199Stjr * 407113199Stjr * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ 408113199Stjr * A B ---C--- D E F 409113199Stjr * 410113199Stjr * A: 'sign' holds this value if present; '\0' otherwise 411113199Stjr * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal 412113199Stjr * C: cp points to the string MMMNNN. Leading and trailing 413113199Stjr * zeros are not in the string and must be added. 414113199Stjr * D: expchar holds this character; '\0' if no exponent, e.g. %f 415113199Stjr * F: at least two digits for decimal, at least one digit for hex 416113199Stjr */ 417187421Sdas wchar_t decimal_point; /* locale specific decimal point */ 418113199Stjr int signflag; /* true if float is negative */ 419113199Stjr union { /* floating point arguments %[aAeEfFgG] */ 420113199Stjr double dbl; 421113199Stjr long double ldbl; 422113199Stjr } fparg; 423103739Stjr int expt; /* integer value of exponent */ 424113199Stjr char expchar; /* exponent character: [eEpP\0] */ 425113199Stjr char *dtoaend; /* pointer to end of converted digits */ 426103739Stjr int expsize; /* character count for expstr */ 427113199Stjr int ndig; /* actual number of digits returned by dtoa */ 428113199Stjr wchar_t expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ 429113199Stjr char *dtoaresult; /* buffer allocated by dtoa */ 430103739Stjr#endif 431103739Stjr u_long ulval; /* integer arguments %[diouxX] */ 432103739Stjr uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ 433103739Stjr int base; /* base for [diouxX] conversion */ 434103739Stjr int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 435103739Stjr int realsz; /* field size expanded by dprec, sign, etc */ 436103739Stjr int size; /* size of converted field or string */ 437103739Stjr int prsize; /* max size of printed field */ 438124887Sdas const char *xdigs; /* digits for [xX] conversion */ 439187277Sdas struct io_state io; /* I/O buffering state */ 440113142Sdas wchar_t buf[BUF]; /* buffer with space for digits of uintmax_t */ 441103739Stjr wchar_t ox[2]; /* space for 0x hex-prefix */ 442103739Stjr union arg *argtable; /* args, built due to positional arg */ 443103739Stjr union arg statargtable [STATIC_ARG_TBL_SIZE]; 444103739Stjr int nextarg; /* 1-based argument index */ 445103739Stjr va_list orgap; /* original argument pointer */ 446103739Stjr wchar_t *convbuf; /* multibyte to wide conversion result */ 447304888Sache int savserr; 448103739Stjr 449124887Sdas static const char xdigs_lower[16] = "0123456789abcdef"; 450124887Sdas static const char xdigs_upper[16] = "0123456789ABCDEF"; 451113199Stjr 452187277Sdas /* BEWARE, these `goto error' on error. */ 453103739Stjr#define PRINT(ptr, len) do { \ 454227753Stheraven if (io_print(&io, (ptr), (len), locale)) \ 455187277Sdas goto error; \ 456103739Stjr} while (0) 457187277Sdas#define PAD(howmany, with) { \ 458227753Stheraven if (io_pad(&io, (howmany), (with), locale)) \ 459187277Sdas goto error; \ 460187277Sdas} 461187277Sdas#define PRINTANDPAD(p, ep, len, with) { \ 462227753Stheraven if (io_printandpad(&io, (p), (ep), (len), (with), locale)) \ 463187277Sdas goto error; \ 464187277Sdas} 465187277Sdas#define FLUSH() { \ 466227753Stheraven if (io_flush(&io, locale)) \ 467187277Sdas goto error; \ 468187277Sdas} 469103739Stjr 470103739Stjr /* 471103739Stjr * Get the argument indexed by nextarg. If the argument table is 472103739Stjr * built, use it to get the argument. If its not, get the next 473103739Stjr * argument (and arguments must be gotten sequentially). 474103739Stjr */ 475103739Stjr#define GETARG(type) \ 476103739Stjr ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ 477103739Stjr (nextarg++, va_arg(ap, type))) 478103739Stjr 479103739Stjr /* 480103739Stjr * To extend shorts properly, we need both signed and unsigned 481103739Stjr * argument extraction methods. 482103739Stjr */ 483103739Stjr#define SARG() \ 484103739Stjr (flags&LONGINT ? GETARG(long) : \ 485103739Stjr flags&SHORTINT ? (long)(short)GETARG(int) : \ 486103739Stjr flags&CHARINT ? (long)(signed char)GETARG(int) : \ 487103739Stjr (long)GETARG(int)) 488103739Stjr#define UARG() \ 489103739Stjr (flags&LONGINT ? GETARG(u_long) : \ 490103739Stjr flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 491103739Stjr flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ 492103739Stjr (u_long)GETARG(u_int)) 493103739Stjr#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) 494103739Stjr#define SJARG() \ 495103739Stjr (flags&INTMAXT ? GETARG(intmax_t) : \ 496189131Sdas flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ 497103739Stjr flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ 498103739Stjr (intmax_t)GETARG(long long)) 499103739Stjr#define UJARG() \ 500103739Stjr (flags&INTMAXT ? GETARG(uintmax_t) : \ 501103739Stjr flags&SIZET ? (uintmax_t)GETARG(size_t) : \ 502103739Stjr flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ 503103739Stjr (uintmax_t)GETARG(unsigned long long)) 504103739Stjr 505103739Stjr /* 506103739Stjr * Get * arguments, including the form *nn$. Preserve the nextarg 507103739Stjr * that the argument can be gotten once the type is determined. 508103739Stjr */ 509103739Stjr#define GETASTER(val) \ 510103739Stjr n2 = 0; \ 511103739Stjr cp = fmt; \ 512103739Stjr while (is_digit(*cp)) { \ 513103739Stjr n2 = 10 * n2 + to_digit(*cp); \ 514103739Stjr cp++; \ 515103739Stjr } \ 516103739Stjr if (*cp == '$') { \ 517103739Stjr int hold = nextarg; \ 518103739Stjr if (argtable == NULL) { \ 519103739Stjr argtable = statargtable; \ 520180106Sdas if (__find_warguments (fmt0, orgap, &argtable)) { \ 521180106Sdas ret = EOF; \ 522180106Sdas goto error; \ 523180106Sdas } \ 524103739Stjr } \ 525103739Stjr nextarg = n2; \ 526103739Stjr val = GETARG (int); \ 527103739Stjr nextarg = hold; \ 528103739Stjr fmt = ++cp; \ 529103739Stjr } else { \ 530103739Stjr val = GETARG (int); \ 531103739Stjr } 532103739Stjr 533103739Stjr 534103739Stjr /* sorry, fwprintf(read_only_file, L"") returns WEOF, not 0 */ 535269482Spfg if (prepwrite(fp) != 0) { 536269482Spfg errno = EBADF; 537103739Stjr return (EOF); 538269482Spfg } 539103739Stjr 540304888Sache savserr = fp->_flags & __SERR; 541304888Sache fp->_flags &= ~__SERR; 542304888Sache 543185904Sdas convbuf = NULL; 544103739Stjr fmt = (wchar_t *)fmt0; 545103739Stjr argtable = NULL; 546103739Stjr nextarg = 1; 547103876Stjr va_copy(orgap, ap); 548187277Sdas io_init(&io, fp); 549103739Stjr ret = 0; 550185904Sdas#ifndef NO_FLOATING_POINT 551227753Stheraven decimal_point = get_decpt(locale); 552185904Sdas#endif 553103739Stjr 554103739Stjr /* 555103739Stjr * Scan the format for conversions (`%' character). 556103739Stjr */ 557103739Stjr for (;;) { 558103739Stjr for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 559103739Stjr /* void */; 560103739Stjr if ((n = fmt - cp) != 0) { 561103739Stjr if ((unsigned)ret + n > INT_MAX) { 562103739Stjr ret = EOF; 563234531Sdas errno = EOVERFLOW; 564103739Stjr goto error; 565103739Stjr } 566103739Stjr PRINT(cp, n); 567103739Stjr ret += n; 568103739Stjr } 569103739Stjr if (ch == '\0') 570103739Stjr goto done; 571103739Stjr fmt++; /* skip over '%' */ 572103739Stjr 573103739Stjr flags = 0; 574103739Stjr dprec = 0; 575103739Stjr width = 0; 576103739Stjr prec = -1; 577187582Sdas gs.grouping = NULL; 578103739Stjr sign = '\0'; 579113199Stjr ox[1] = '\0'; 580103739Stjr 581103739Stjrrflag: ch = *fmt++; 582103739Stjrreswitch: switch (ch) { 583103739Stjr case ' ': 584103739Stjr /*- 585103739Stjr * ``If the space and + flags both appear, the space 586103739Stjr * flag will be ignored.'' 587103739Stjr * -- ANSI X3J11 588103739Stjr */ 589103739Stjr if (!sign) 590103739Stjr sign = ' '; 591103739Stjr goto rflag; 592103739Stjr case '#': 593103739Stjr flags |= ALT; 594103739Stjr goto rflag; 595103739Stjr case '*': 596103739Stjr /*- 597103739Stjr * ``A negative field width argument is taken as a 598103739Stjr * - flag followed by a positive field width.'' 599103739Stjr * -- ANSI X3J11 600103739Stjr * They don't exclude field widths read from args. 601103739Stjr */ 602103739Stjr GETASTER (width); 603103739Stjr if (width >= 0) 604103739Stjr goto rflag; 605103739Stjr width = -width; 606103739Stjr /* FALLTHROUGH */ 607103739Stjr case '-': 608103739Stjr flags |= LADJUST; 609103739Stjr goto rflag; 610103739Stjr case '+': 611103739Stjr sign = '+'; 612103739Stjr goto rflag; 613103739Stjr case '\'': 614103739Stjr flags |= GROUPING; 615103739Stjr goto rflag; 616103739Stjr case '.': 617103739Stjr if ((ch = *fmt++) == '*') { 618113199Stjr GETASTER (prec); 619103739Stjr goto rflag; 620103739Stjr } 621113199Stjr prec = 0; 622103739Stjr while (is_digit(ch)) { 623113199Stjr prec = 10 * prec + to_digit(ch); 624103739Stjr ch = *fmt++; 625103739Stjr } 626103739Stjr goto reswitch; 627103739Stjr case '0': 628103739Stjr /*- 629103739Stjr * ``Note that 0 is taken as a flag, not as the 630103739Stjr * beginning of a field width.'' 631103739Stjr * -- ANSI X3J11 632103739Stjr */ 633103739Stjr flags |= ZEROPAD; 634103739Stjr goto rflag; 635103739Stjr case '1': case '2': case '3': case '4': 636103739Stjr case '5': case '6': case '7': case '8': case '9': 637103739Stjr n = 0; 638103739Stjr do { 639103739Stjr n = 10 * n + to_digit(ch); 640103739Stjr ch = *fmt++; 641103739Stjr } while (is_digit(ch)); 642103739Stjr if (ch == '$') { 643103739Stjr nextarg = n; 644103739Stjr if (argtable == NULL) { 645103739Stjr argtable = statargtable; 646180106Sdas if (__find_warguments (fmt0, orgap, 647180106Sdas &argtable)) { 648180106Sdas ret = EOF; 649180106Sdas goto error; 650180106Sdas } 651103739Stjr } 652103739Stjr goto rflag; 653103739Stjr } 654103739Stjr width = n; 655103739Stjr goto reswitch; 656128821Sdas#ifndef NO_FLOATING_POINT 657103739Stjr case 'L': 658103739Stjr flags |= LONGDBL; 659103739Stjr goto rflag; 660103739Stjr#endif 661103739Stjr case 'h': 662103739Stjr if (flags & SHORTINT) { 663103739Stjr flags &= ~SHORTINT; 664103739Stjr flags |= CHARINT; 665103739Stjr } else 666103739Stjr flags |= SHORTINT; 667103739Stjr goto rflag; 668103739Stjr case 'j': 669103739Stjr flags |= INTMAXT; 670103739Stjr goto rflag; 671103739Stjr case 'l': 672103739Stjr if (flags & LONGINT) { 673103739Stjr flags &= ~LONGINT; 674103739Stjr flags |= LLONGINT; 675103739Stjr } else 676103739Stjr flags |= LONGINT; 677103739Stjr goto rflag; 678103739Stjr case 'q': 679103739Stjr flags |= LLONGINT; /* not necessarily */ 680103739Stjr goto rflag; 681103739Stjr case 't': 682103739Stjr flags |= PTRDIFFT; 683103739Stjr goto rflag; 684103739Stjr case 'z': 685103739Stjr flags |= SIZET; 686103739Stjr goto rflag; 687105204Stjr case 'C': 688105204Stjr flags |= LONGINT; 689105204Stjr /*FALLTHROUGH*/ 690103739Stjr case 'c': 691103739Stjr if (flags & LONGINT) 692103739Stjr *(cp = buf) = (wchar_t)GETARG(wint_t); 693103739Stjr else 694103739Stjr *(cp = buf) = (wchar_t)btowc(GETARG(int)); 695103739Stjr size = 1; 696103739Stjr sign = '\0'; 697103739Stjr break; 698103739Stjr case 'D': 699103739Stjr flags |= LONGINT; 700103739Stjr /*FALLTHROUGH*/ 701103739Stjr case 'd': 702103739Stjr case 'i': 703103739Stjr if (flags & INTMAX_SIZE) { 704103739Stjr ujval = SJARG(); 705103739Stjr if ((intmax_t)ujval < 0) { 706103739Stjr ujval = -ujval; 707103739Stjr sign = '-'; 708103739Stjr } 709103739Stjr } else { 710103739Stjr ulval = SARG(); 711103739Stjr if ((long)ulval < 0) { 712103739Stjr ulval = -ulval; 713103739Stjr sign = '-'; 714103739Stjr } 715103739Stjr } 716103739Stjr base = 10; 717103739Stjr goto number; 718128821Sdas#ifndef NO_FLOATING_POINT 719103739Stjr case 'a': 720103739Stjr case 'A': 721113199Stjr if (ch == 'a') { 722113199Stjr ox[1] = 'x'; 723113199Stjr xdigs = xdigs_lower; 724113199Stjr expchar = 'p'; 725113199Stjr } else { 726113199Stjr ox[1] = 'X'; 727113199Stjr xdigs = xdigs_upper; 728113199Stjr expchar = 'P'; 729113199Stjr } 730124887Sdas if (prec >= 0) 731124887Sdas prec++; 732113199Stjr if (flags & LONGDBL) { 733124887Sdas fparg.ldbl = GETARG(long double); 734113199Stjr dtoaresult = 735113199Stjr __hldtoa(fparg.ldbl, xdigs, prec, 736113199Stjr &expt, &signflag, &dtoaend); 737113199Stjr } else { 738113199Stjr fparg.dbl = GETARG(double); 739113199Stjr dtoaresult = 740113199Stjr __hdtoa(fparg.dbl, xdigs, prec, 741113199Stjr &expt, &signflag, &dtoaend); 742113199Stjr } 743124887Sdas if (prec < 0) 744124887Sdas prec = dtoaend - dtoaresult; 745124887Sdas if (expt == INT_MAX) 746124887Sdas ox[1] = '\0'; 747113199Stjr if (convbuf != NULL) 748113199Stjr free(convbuf); 749124887Sdas ndig = dtoaend - dtoaresult; 750113199Stjr cp = convbuf = __mbsconv(dtoaresult, -1); 751113199Stjr freedtoa(dtoaresult); 752124887Sdas goto fp_common; 753103739Stjr case 'e': 754103739Stjr case 'E': 755113199Stjr expchar = ch; 756113199Stjr if (prec < 0) /* account for digit before decpt */ 757113199Stjr prec = DEFPREC + 1; 758113199Stjr else 759113199Stjr prec++; 760113199Stjr goto fp_begin; 761103739Stjr case 'f': 762103739Stjr case 'F': 763113199Stjr expchar = '\0'; 764103739Stjr goto fp_begin; 765103739Stjr case 'g': 766103739Stjr case 'G': 767113199Stjr expchar = ch - ('g' - 'e'); 768103739Stjr if (prec == 0) 769103739Stjr prec = 1; 770113199Stjrfp_begin: 771113199Stjr if (prec < 0) 772103739Stjr prec = DEFPREC; 773113199Stjr if (convbuf != NULL) 774113199Stjr free(convbuf); 775113199Stjr if (flags & LONGDBL) { 776113199Stjr fparg.ldbl = GETARG(long double); 777113199Stjr dtoaresult = 778113199Stjr __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, 779113199Stjr &expt, &signflag, &dtoaend); 780113199Stjr } else { 781113199Stjr fparg.dbl = GETARG(double); 782113199Stjr dtoaresult = 783113199Stjr dtoa(fparg.dbl, expchar ? 2 : 3, prec, 784113199Stjr &expt, &signflag, &dtoaend); 785113199Stjr if (expt == 9999) 786113199Stjr expt = INT_MAX; 787103739Stjr } 788113199Stjr ndig = dtoaend - dtoaresult; 789113199Stjr cp = convbuf = __mbsconv(dtoaresult, -1); 790113199Stjr freedtoa(dtoaresult); 791124887Sdasfp_common: 792113199Stjr if (signflag) 793113199Stjr sign = '-'; 794113199Stjr if (expt == INT_MAX) { /* inf or nan */ 795113199Stjr if (*cp == 'N') { 796113199Stjr cp = (ch >= 'a') ? L"nan" : L"NAN"; 797113199Stjr sign = '\0'; 798113199Stjr } else 799113199Stjr cp = (ch >= 'a') ? L"inf" : L"INF"; 800103739Stjr size = 3; 801174733Sdas flags &= ~ZEROPAD; 802103739Stjr break; 803103739Stjr } 804103739Stjr flags |= FPT; 805103739Stjr if (ch == 'g' || ch == 'G') { 806113199Stjr if (expt > -4 && expt <= prec) { 807113199Stjr /* Make %[gG] smell like %[fF] */ 808113199Stjr expchar = '\0'; 809113199Stjr if (flags & ALT) 810113199Stjr prec -= expt; 811113199Stjr else 812113199Stjr prec = ndig - expt; 813113199Stjr if (prec < 0) 814113199Stjr prec = 0; 815113723Sdas } else { 816113723Sdas /* 817113723Sdas * Make %[gG] smell like %[eE], but 818113723Sdas * trim trailing zeroes if no # flag. 819113723Sdas */ 820113723Sdas if (!(flags & ALT)) 821113723Sdas prec = ndig; 822113199Stjr } 823103739Stjr } 824113199Stjr if (expchar) { 825113199Stjr expsize = exponent(expstr, expt - 1, expchar); 826113199Stjr size = expsize + prec; 827113199Stjr if (prec > 1 || flags & ALT) 828103739Stjr ++size; 829113199Stjr } else { 830113470Stjr /* space for digits before decimal point */ 831113470Stjr if (expt > 0) 832103739Stjr size = expt; 833113470Stjr else /* "0" */ 834113470Stjr size = 1; 835113470Stjr /* space for decimal pt and following digits */ 836113470Stjr if (prec || flags & ALT) 837113470Stjr size += prec + 1; 838187582Sdas if ((flags & GROUPING) && expt > 0) 839227753Stheraven size += grouping_init(&gs, expt, locale); 840113199Stjr } 841103739Stjr break; 842128821Sdas#endif /* !NO_FLOATING_POINT */ 843103739Stjr case 'n': 844103739Stjr /* 845103739Stjr * Assignment-like behavior is specified if the 846103739Stjr * value overflows or is otherwise unrepresentable. 847103739Stjr * C99 says to use `signed char' for %hhn conversions. 848103739Stjr */ 849103739Stjr if (flags & LLONGINT) 850103739Stjr *GETARG(long long *) = ret; 851103739Stjr else if (flags & SIZET) 852103739Stjr *GETARG(ssize_t *) = (ssize_t)ret; 853103739Stjr else if (flags & PTRDIFFT) 854103739Stjr *GETARG(ptrdiff_t *) = ret; 855103739Stjr else if (flags & INTMAXT) 856103739Stjr *GETARG(intmax_t *) = ret; 857103739Stjr else if (flags & LONGINT) 858103739Stjr *GETARG(long *) = ret; 859103739Stjr else if (flags & SHORTINT) 860103739Stjr *GETARG(short *) = ret; 861103739Stjr else if (flags & CHARINT) 862103739Stjr *GETARG(signed char *) = ret; 863103739Stjr else 864103739Stjr *GETARG(int *) = ret; 865103739Stjr continue; /* no output */ 866103739Stjr case 'O': 867103739Stjr flags |= LONGINT; 868103739Stjr /*FALLTHROUGH*/ 869103739Stjr case 'o': 870103739Stjr if (flags & INTMAX_SIZE) 871103739Stjr ujval = UJARG(); 872103739Stjr else 873103739Stjr ulval = UARG(); 874103739Stjr base = 8; 875103739Stjr goto nosign; 876103739Stjr case 'p': 877103739Stjr /*- 878103739Stjr * ``The argument shall be a pointer to void. The 879103739Stjr * value of the pointer is converted to a sequence 880103739Stjr * of printable characters, in an implementation- 881103739Stjr * defined manner.'' 882103739Stjr * -- ANSI X3J11 883103739Stjr */ 884103739Stjr ujval = (uintmax_t)(uintptr_t)GETARG(void *); 885103739Stjr base = 16; 886113199Stjr xdigs = xdigs_lower; 887113199Stjr flags = flags | INTMAXT; 888113199Stjr ox[1] = 'x'; 889103739Stjr goto nosign; 890105204Stjr case 'S': 891105204Stjr flags |= LONGINT; 892105204Stjr /*FALLTHROUGH*/ 893103739Stjr case 's': 894103739Stjr if (flags & LONGINT) { 895103739Stjr if ((cp = GETARG(wchar_t *)) == NULL) 896103739Stjr cp = L"(null)"; 897103739Stjr } else { 898103739Stjr char *mbp; 899103739Stjr 900103739Stjr if (convbuf != NULL) 901103739Stjr free(convbuf); 902103739Stjr if ((mbp = GETARG(char *)) == NULL) 903103739Stjr cp = L"(null)"; 904103739Stjr else { 905103739Stjr convbuf = __mbsconv(mbp, prec); 906105234Stjr if (convbuf == NULL) { 907105234Stjr fp->_flags |= __SERR; 908103739Stjr goto error; 909105234Stjr } 910103739Stjr cp = convbuf; 911103739Stjr } 912103739Stjr } 913189138Sdas size = (prec >= 0) ? wcsnlen(cp, prec) : wcslen(cp); 914103739Stjr sign = '\0'; 915103739Stjr break; 916103739Stjr case 'U': 917103739Stjr flags |= LONGINT; 918103739Stjr /*FALLTHROUGH*/ 919103739Stjr case 'u': 920103739Stjr if (flags & INTMAX_SIZE) 921103739Stjr ujval = UJARG(); 922103739Stjr else 923103739Stjr ulval = UARG(); 924103739Stjr base = 10; 925103739Stjr goto nosign; 926103739Stjr case 'X': 927113199Stjr xdigs = xdigs_upper; 928103739Stjr goto hex; 929103739Stjr case 'x': 930113199Stjr xdigs = xdigs_lower; 931103739Stjrhex: 932103739Stjr if (flags & INTMAX_SIZE) 933103739Stjr ujval = UJARG(); 934103739Stjr else 935103739Stjr ulval = UARG(); 936103739Stjr base = 16; 937103739Stjr /* leading 0x/X only if non-zero */ 938103739Stjr if (flags & ALT && 939103739Stjr (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) 940113199Stjr ox[1] = ch; 941103739Stjr 942103739Stjr flags &= ~GROUPING; 943103739Stjr /* unsigned conversions */ 944103739Stjrnosign: sign = '\0'; 945103739Stjr /*- 946103739Stjr * ``... diouXx conversions ... if a precision is 947103739Stjr * specified, the 0 flag will be ignored.'' 948103739Stjr * -- ANSI X3J11 949103739Stjr */ 950103739Stjrnumber: if ((dprec = prec) >= 0) 951103739Stjr flags &= ~ZEROPAD; 952103739Stjr 953103739Stjr /*- 954103739Stjr * ``The result of converting a zero value with an 955103739Stjr * explicit precision of zero is no characters.'' 956103739Stjr * -- ANSI X3J11 957145172Sdas * 958145172Sdas * ``The C Standard is clear enough as is. The call 959145172Sdas * printf("%#.0o", 0) should print 0.'' 960145172Sdas * -- Defect Report #151 961103739Stjr */ 962103739Stjr cp = buf + BUF; 963103739Stjr if (flags & INTMAX_SIZE) { 964145172Sdas if (ujval != 0 || prec != 0 || 965145172Sdas (flags & ALT && base == 8)) 966103739Stjr cp = __ujtoa(ujval, cp, base, 967187582Sdas flags & ALT, xdigs); 968103739Stjr } else { 969145172Sdas if (ulval != 0 || prec != 0 || 970145172Sdas (flags & ALT && base == 8)) 971103739Stjr cp = __ultoa(ulval, cp, base, 972187582Sdas flags & ALT, xdigs); 973103739Stjr } 974103739Stjr size = buf + BUF - cp; 975113142Sdas if (size > BUF) /* should never happen */ 976113142Sdas abort(); 977187582Sdas if ((flags & GROUPING) && size != 0) 978227753Stheraven size += grouping_init(&gs, size, locale); 979103739Stjr break; 980103739Stjr default: /* "%?" prints ?, unless ? is NUL */ 981103739Stjr if (ch == '\0') 982103739Stjr goto done; 983103739Stjr /* pretend it was %c with argument ch */ 984103739Stjr cp = buf; 985103739Stjr *cp = ch; 986103739Stjr size = 1; 987103739Stjr sign = '\0'; 988103739Stjr break; 989103739Stjr } 990103739Stjr 991103739Stjr /* 992103739Stjr * All reasonable formats wind up here. At this point, `cp' 993103739Stjr * points to a string which (if not flags&LADJUST) should be 994103739Stjr * padded out to `width' places. If flags&ZEROPAD, it should 995103739Stjr * first be prefixed by any sign or other prefix; otherwise, 996103739Stjr * it should be blank padded before the prefix is emitted. 997103739Stjr * After any left-hand padding and prefixing, emit zeroes 998103739Stjr * required by a decimal [diouxX] precision, then print the 999103739Stjr * string proper, then emit zeroes required by any leftover 1000103739Stjr * floating precision; finally, if LADJUST, pad with blanks. 1001103739Stjr * 1002103739Stjr * Compute actual size, so we know how much to pad. 1003103739Stjr * size excludes decimal prec; realsz includes it. 1004103739Stjr */ 1005103739Stjr realsz = dprec > size ? dprec : size; 1006103739Stjr if (sign) 1007103739Stjr realsz++; 1008124887Sdas if (ox[1]) 1009103739Stjr realsz += 2; 1010103739Stjr 1011103739Stjr prsize = width > realsz ? width : realsz; 1012103739Stjr if ((unsigned)ret + prsize > INT_MAX) { 1013103739Stjr ret = EOF; 1014234531Sdas errno = EOVERFLOW; 1015103739Stjr goto error; 1016103739Stjr } 1017103739Stjr 1018103739Stjr /* right-adjusting blank padding */ 1019103739Stjr if ((flags & (LADJUST|ZEROPAD)) == 0) 1020103739Stjr PAD(width - realsz, blanks); 1021103739Stjr 1022103739Stjr /* prefix */ 1023124887Sdas if (sign) 1024103739Stjr PRINT(&sign, 1); 1025124887Sdas 1026124887Sdas if (ox[1]) { /* ox[1] is either x, X, or \0 */ 1027103739Stjr ox[0] = '0'; 1028103739Stjr PRINT(ox, 2); 1029103739Stjr } 1030103739Stjr 1031103739Stjr /* right-adjusting zero padding */ 1032103739Stjr if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 1033103739Stjr PAD(width - realsz, zeroes); 1034103739Stjr 1035103739Stjr /* the string or number proper */ 1036128821Sdas#ifndef NO_FLOATING_POINT 1037103739Stjr if ((flags & FPT) == 0) { 1038187582Sdas#endif 1039187582Sdas /* leading zeroes from decimal precision */ 1040187582Sdas PAD(dprec - size, zeroes); 1041187582Sdas if (gs.grouping) { 1042227753Stheraven if (grouping_print(&gs, &io, cp, buf+BUF, locale) < 0) 1043187582Sdas goto error; 1044187582Sdas } else { 1045187582Sdas PRINT(cp, size); 1046187582Sdas } 1047187582Sdas#ifndef NO_FLOATING_POINT 1048103739Stjr } else { /* glue together f_p fragments */ 1049113199Stjr if (!expchar) { /* %[fF] or sufficiently short %[gG] */ 1050113199Stjr if (expt <= 0) { 1051113470Stjr PRINT(zeroes, 1); 1052187421Sdas if (prec || flags & ALT) 1053187421Sdas PRINT(&decimal_point, 1); 1054103739Stjr PAD(-expt, zeroes); 1055113199Stjr /* already handled initial 0's */ 1056113199Stjr prec += expt; 1057103739Stjr } else { 1058187582Sdas if (gs.grouping) { 1059187582Sdas n = grouping_print(&gs, &io, 1060227753Stheraven cp, convbuf + ndig, locale); 1061187582Sdas if (n < 0) 1062187582Sdas goto error; 1063187582Sdas cp += n; 1064187582Sdas } else { 1065187582Sdas PRINTANDPAD(cp, convbuf + ndig, 1066187582Sdas expt, zeroes); 1067187582Sdas cp += expt; 1068113199Stjr } 1069187421Sdas if (prec || flags & ALT) 1070187421Sdas PRINT(&decimal_point, 1); 1071103739Stjr } 1072113199Stjr PRINTANDPAD(cp, convbuf + ndig, prec, zeroes); 1073113199Stjr } else { /* %[eE] or sufficiently long %[gG] */ 1074113199Stjr if (prec > 1 || flags & ALT) { 1075113199Stjr buf[0] = *cp++; 1076187421Sdas buf[1] = decimal_point; 1077113199Stjr PRINT(buf, 2); 1078113199Stjr PRINT(cp, ndig-1); 1079113199Stjr PAD(prec - ndig, zeroes); 1080103739Stjr } else /* XeYYY */ 1081103739Stjr PRINT(cp, 1); 1082103739Stjr PRINT(expstr, expsize); 1083103739Stjr } 1084103739Stjr } 1085103739Stjr#endif 1086103739Stjr /* left-adjusting padding (always blank) */ 1087103739Stjr if (flags & LADJUST) 1088103739Stjr PAD(width - realsz, blanks); 1089103739Stjr 1090103739Stjr /* finally, adjust ret */ 1091103739Stjr ret += prsize; 1092187277Sdas 1093187277Sdas FLUSH(); /* copy out the I/O vectors */ 1094103739Stjr } 1095103739Stjrdone: 1096187277Sdas FLUSH(); 1097103739Stjrerror: 1098134332Sdes va_end(orgap); 1099103739Stjr if (convbuf != NULL) 1100103739Stjr free(convbuf); 1101103739Stjr if (__sferror(fp)) 1102103739Stjr ret = EOF; 1103304888Sache else 1104304888Sache fp->_flags |= savserr; 1105103739Stjr if ((argtable != NULL) && (argtable != statargtable)) 1106103739Stjr free (argtable); 1107103739Stjr return (ret); 1108103739Stjr /* NOTREACHED */ 1109103739Stjr} 1110