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$"); 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); 359103739Stjr FLOCKFILE(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); 366103739Stjr FUNLOCKFILE(fp); 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 */ 447103739Stjr 448124887Sdas static const char xdigs_lower[16] = "0123456789abcdef"; 449124887Sdas static const char xdigs_upper[16] = "0123456789ABCDEF"; 450113199Stjr 451187277Sdas /* BEWARE, these `goto error' on error. */ 452103739Stjr#define PRINT(ptr, len) do { \ 453227753Stheraven if (io_print(&io, (ptr), (len), locale)) \ 454187277Sdas goto error; \ 455103739Stjr} while (0) 456187277Sdas#define PAD(howmany, with) { \ 457227753Stheraven if (io_pad(&io, (howmany), (with), locale)) \ 458187277Sdas goto error; \ 459187277Sdas} 460187277Sdas#define PRINTANDPAD(p, ep, len, with) { \ 461227753Stheraven if (io_printandpad(&io, (p), (ep), (len), (with), locale)) \ 462187277Sdas goto error; \ 463187277Sdas} 464187277Sdas#define FLUSH() { \ 465227753Stheraven if (io_flush(&io, locale)) \ 466187277Sdas goto error; \ 467187277Sdas} 468103739Stjr 469103739Stjr /* 470103739Stjr * Get the argument indexed by nextarg. If the argument table is 471103739Stjr * built, use it to get the argument. If its not, get the next 472103739Stjr * argument (and arguments must be gotten sequentially). 473103739Stjr */ 474103739Stjr#define GETARG(type) \ 475103739Stjr ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ 476103739Stjr (nextarg++, va_arg(ap, type))) 477103739Stjr 478103739Stjr /* 479103739Stjr * To extend shorts properly, we need both signed and unsigned 480103739Stjr * argument extraction methods. 481103739Stjr */ 482103739Stjr#define SARG() \ 483103739Stjr (flags&LONGINT ? GETARG(long) : \ 484103739Stjr flags&SHORTINT ? (long)(short)GETARG(int) : \ 485103739Stjr flags&CHARINT ? (long)(signed char)GETARG(int) : \ 486103739Stjr (long)GETARG(int)) 487103739Stjr#define UARG() \ 488103739Stjr (flags&LONGINT ? GETARG(u_long) : \ 489103739Stjr flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 490103739Stjr flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ 491103739Stjr (u_long)GETARG(u_int)) 492103739Stjr#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) 493103739Stjr#define SJARG() \ 494103739Stjr (flags&INTMAXT ? GETARG(intmax_t) : \ 495189131Sdas flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ 496103739Stjr flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ 497103739Stjr (intmax_t)GETARG(long long)) 498103739Stjr#define UJARG() \ 499103739Stjr (flags&INTMAXT ? GETARG(uintmax_t) : \ 500103739Stjr flags&SIZET ? (uintmax_t)GETARG(size_t) : \ 501103739Stjr flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ 502103739Stjr (uintmax_t)GETARG(unsigned long long)) 503103739Stjr 504103739Stjr /* 505103739Stjr * Get * arguments, including the form *nn$. Preserve the nextarg 506103739Stjr * that the argument can be gotten once the type is determined. 507103739Stjr */ 508103739Stjr#define GETASTER(val) \ 509103739Stjr n2 = 0; \ 510103739Stjr cp = fmt; \ 511103739Stjr while (is_digit(*cp)) { \ 512103739Stjr n2 = 10 * n2 + to_digit(*cp); \ 513103739Stjr cp++; \ 514103739Stjr } \ 515103739Stjr if (*cp == '$') { \ 516103739Stjr int hold = nextarg; \ 517103739Stjr if (argtable == NULL) { \ 518103739Stjr argtable = statargtable; \ 519180106Sdas if (__find_warguments (fmt0, orgap, &argtable)) { \ 520180106Sdas ret = EOF; \ 521180106Sdas goto error; \ 522180106Sdas } \ 523103739Stjr } \ 524103739Stjr nextarg = n2; \ 525103739Stjr val = GETARG (int); \ 526103739Stjr nextarg = hold; \ 527103739Stjr fmt = ++cp; \ 528103739Stjr } else { \ 529103739Stjr val = GETARG (int); \ 530103739Stjr } 531103739Stjr 532103739Stjr 533103739Stjr /* sorry, fwprintf(read_only_file, L"") returns WEOF, not 0 */ 534130231Sdas if (prepwrite(fp) != 0) 535103739Stjr return (EOF); 536103739Stjr 537185904Sdas convbuf = NULL; 538103739Stjr fmt = (wchar_t *)fmt0; 539103739Stjr argtable = NULL; 540103739Stjr nextarg = 1; 541103876Stjr va_copy(orgap, ap); 542187277Sdas io_init(&io, fp); 543103739Stjr ret = 0; 544185904Sdas#ifndef NO_FLOATING_POINT 545227753Stheraven decimal_point = get_decpt(locale); 546185904Sdas#endif 547103739Stjr 548103739Stjr /* 549103739Stjr * Scan the format for conversions (`%' character). 550103739Stjr */ 551103739Stjr for (;;) { 552103739Stjr for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 553103739Stjr /* void */; 554103739Stjr if ((n = fmt - cp) != 0) { 555103739Stjr if ((unsigned)ret + n > INT_MAX) { 556103739Stjr ret = EOF; 557234531Sdas errno = EOVERFLOW; 558103739Stjr goto error; 559103739Stjr } 560103739Stjr PRINT(cp, n); 561103739Stjr ret += n; 562103739Stjr } 563103739Stjr if (ch == '\0') 564103739Stjr goto done; 565103739Stjr fmt++; /* skip over '%' */ 566103739Stjr 567103739Stjr flags = 0; 568103739Stjr dprec = 0; 569103739Stjr width = 0; 570103739Stjr prec = -1; 571187582Sdas gs.grouping = NULL; 572103739Stjr sign = '\0'; 573113199Stjr ox[1] = '\0'; 574103739Stjr 575103739Stjrrflag: ch = *fmt++; 576103739Stjrreswitch: switch (ch) { 577103739Stjr case ' ': 578103739Stjr /*- 579103739Stjr * ``If the space and + flags both appear, the space 580103739Stjr * flag will be ignored.'' 581103739Stjr * -- ANSI X3J11 582103739Stjr */ 583103739Stjr if (!sign) 584103739Stjr sign = ' '; 585103739Stjr goto rflag; 586103739Stjr case '#': 587103739Stjr flags |= ALT; 588103739Stjr goto rflag; 589103739Stjr case '*': 590103739Stjr /*- 591103739Stjr * ``A negative field width argument is taken as a 592103739Stjr * - flag followed by a positive field width.'' 593103739Stjr * -- ANSI X3J11 594103739Stjr * They don't exclude field widths read from args. 595103739Stjr */ 596103739Stjr GETASTER (width); 597103739Stjr if (width >= 0) 598103739Stjr goto rflag; 599103739Stjr width = -width; 600103739Stjr /* FALLTHROUGH */ 601103739Stjr case '-': 602103739Stjr flags |= LADJUST; 603103739Stjr goto rflag; 604103739Stjr case '+': 605103739Stjr sign = '+'; 606103739Stjr goto rflag; 607103739Stjr case '\'': 608103739Stjr flags |= GROUPING; 609103739Stjr goto rflag; 610103739Stjr case '.': 611103739Stjr if ((ch = *fmt++) == '*') { 612113199Stjr GETASTER (prec); 613103739Stjr goto rflag; 614103739Stjr } 615113199Stjr prec = 0; 616103739Stjr while (is_digit(ch)) { 617113199Stjr prec = 10 * prec + to_digit(ch); 618103739Stjr ch = *fmt++; 619103739Stjr } 620103739Stjr goto reswitch; 621103739Stjr case '0': 622103739Stjr /*- 623103739Stjr * ``Note that 0 is taken as a flag, not as the 624103739Stjr * beginning of a field width.'' 625103739Stjr * -- ANSI X3J11 626103739Stjr */ 627103739Stjr flags |= ZEROPAD; 628103739Stjr goto rflag; 629103739Stjr case '1': case '2': case '3': case '4': 630103739Stjr case '5': case '6': case '7': case '8': case '9': 631103739Stjr n = 0; 632103739Stjr do { 633103739Stjr n = 10 * n + to_digit(ch); 634103739Stjr ch = *fmt++; 635103739Stjr } while (is_digit(ch)); 636103739Stjr if (ch == '$') { 637103739Stjr nextarg = n; 638103739Stjr if (argtable == NULL) { 639103739Stjr argtable = statargtable; 640180106Sdas if (__find_warguments (fmt0, orgap, 641180106Sdas &argtable)) { 642180106Sdas ret = EOF; 643180106Sdas goto error; 644180106Sdas } 645103739Stjr } 646103739Stjr goto rflag; 647103739Stjr } 648103739Stjr width = n; 649103739Stjr goto reswitch; 650128821Sdas#ifndef NO_FLOATING_POINT 651103739Stjr case 'L': 652103739Stjr flags |= LONGDBL; 653103739Stjr goto rflag; 654103739Stjr#endif 655103739Stjr case 'h': 656103739Stjr if (flags & SHORTINT) { 657103739Stjr flags &= ~SHORTINT; 658103739Stjr flags |= CHARINT; 659103739Stjr } else 660103739Stjr flags |= SHORTINT; 661103739Stjr goto rflag; 662103739Stjr case 'j': 663103739Stjr flags |= INTMAXT; 664103739Stjr goto rflag; 665103739Stjr case 'l': 666103739Stjr if (flags & LONGINT) { 667103739Stjr flags &= ~LONGINT; 668103739Stjr flags |= LLONGINT; 669103739Stjr } else 670103739Stjr flags |= LONGINT; 671103739Stjr goto rflag; 672103739Stjr case 'q': 673103739Stjr flags |= LLONGINT; /* not necessarily */ 674103739Stjr goto rflag; 675103739Stjr case 't': 676103739Stjr flags |= PTRDIFFT; 677103739Stjr goto rflag; 678103739Stjr case 'z': 679103739Stjr flags |= SIZET; 680103739Stjr goto rflag; 681105204Stjr case 'C': 682105204Stjr flags |= LONGINT; 683105204Stjr /*FALLTHROUGH*/ 684103739Stjr case 'c': 685103739Stjr if (flags & LONGINT) 686103739Stjr *(cp = buf) = (wchar_t)GETARG(wint_t); 687103739Stjr else 688103739Stjr *(cp = buf) = (wchar_t)btowc(GETARG(int)); 689103739Stjr size = 1; 690103739Stjr sign = '\0'; 691103739Stjr break; 692103739Stjr case 'D': 693103739Stjr flags |= LONGINT; 694103739Stjr /*FALLTHROUGH*/ 695103739Stjr case 'd': 696103739Stjr case 'i': 697103739Stjr if (flags & INTMAX_SIZE) { 698103739Stjr ujval = SJARG(); 699103739Stjr if ((intmax_t)ujval < 0) { 700103739Stjr ujval = -ujval; 701103739Stjr sign = '-'; 702103739Stjr } 703103739Stjr } else { 704103739Stjr ulval = SARG(); 705103739Stjr if ((long)ulval < 0) { 706103739Stjr ulval = -ulval; 707103739Stjr sign = '-'; 708103739Stjr } 709103739Stjr } 710103739Stjr base = 10; 711103739Stjr goto number; 712128821Sdas#ifndef NO_FLOATING_POINT 713103739Stjr case 'a': 714103739Stjr case 'A': 715113199Stjr if (ch == 'a') { 716113199Stjr ox[1] = 'x'; 717113199Stjr xdigs = xdigs_lower; 718113199Stjr expchar = 'p'; 719113199Stjr } else { 720113199Stjr ox[1] = 'X'; 721113199Stjr xdigs = xdigs_upper; 722113199Stjr expchar = 'P'; 723113199Stjr } 724124887Sdas if (prec >= 0) 725124887Sdas prec++; 726113199Stjr if (flags & LONGDBL) { 727124887Sdas fparg.ldbl = GETARG(long double); 728113199Stjr dtoaresult = 729113199Stjr __hldtoa(fparg.ldbl, xdigs, prec, 730113199Stjr &expt, &signflag, &dtoaend); 731113199Stjr } else { 732113199Stjr fparg.dbl = GETARG(double); 733113199Stjr dtoaresult = 734113199Stjr __hdtoa(fparg.dbl, xdigs, prec, 735113199Stjr &expt, &signflag, &dtoaend); 736113199Stjr } 737124887Sdas if (prec < 0) 738124887Sdas prec = dtoaend - dtoaresult; 739124887Sdas if (expt == INT_MAX) 740124887Sdas ox[1] = '\0'; 741113199Stjr if (convbuf != NULL) 742113199Stjr free(convbuf); 743124887Sdas ndig = dtoaend - dtoaresult; 744113199Stjr cp = convbuf = __mbsconv(dtoaresult, -1); 745113199Stjr freedtoa(dtoaresult); 746124887Sdas goto fp_common; 747103739Stjr case 'e': 748103739Stjr case 'E': 749113199Stjr expchar = ch; 750113199Stjr if (prec < 0) /* account for digit before decpt */ 751113199Stjr prec = DEFPREC + 1; 752113199Stjr else 753113199Stjr prec++; 754113199Stjr goto fp_begin; 755103739Stjr case 'f': 756103739Stjr case 'F': 757113199Stjr expchar = '\0'; 758103739Stjr goto fp_begin; 759103739Stjr case 'g': 760103739Stjr case 'G': 761113199Stjr expchar = ch - ('g' - 'e'); 762103739Stjr if (prec == 0) 763103739Stjr prec = 1; 764113199Stjrfp_begin: 765113199Stjr if (prec < 0) 766103739Stjr prec = DEFPREC; 767113199Stjr if (convbuf != NULL) 768113199Stjr free(convbuf); 769113199Stjr if (flags & LONGDBL) { 770113199Stjr fparg.ldbl = GETARG(long double); 771113199Stjr dtoaresult = 772113199Stjr __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, 773113199Stjr &expt, &signflag, &dtoaend); 774113199Stjr } else { 775113199Stjr fparg.dbl = GETARG(double); 776113199Stjr dtoaresult = 777113199Stjr dtoa(fparg.dbl, expchar ? 2 : 3, prec, 778113199Stjr &expt, &signflag, &dtoaend); 779113199Stjr if (expt == 9999) 780113199Stjr expt = INT_MAX; 781103739Stjr } 782113199Stjr ndig = dtoaend - dtoaresult; 783113199Stjr cp = convbuf = __mbsconv(dtoaresult, -1); 784113199Stjr freedtoa(dtoaresult); 785124887Sdasfp_common: 786113199Stjr if (signflag) 787113199Stjr sign = '-'; 788113199Stjr if (expt == INT_MAX) { /* inf or nan */ 789113199Stjr if (*cp == 'N') { 790113199Stjr cp = (ch >= 'a') ? L"nan" : L"NAN"; 791113199Stjr sign = '\0'; 792113199Stjr } else 793113199Stjr cp = (ch >= 'a') ? L"inf" : L"INF"; 794103739Stjr size = 3; 795174733Sdas flags &= ~ZEROPAD; 796103739Stjr break; 797103739Stjr } 798103739Stjr flags |= FPT; 799103739Stjr if (ch == 'g' || ch == 'G') { 800113199Stjr if (expt > -4 && expt <= prec) { 801113199Stjr /* Make %[gG] smell like %[fF] */ 802113199Stjr expchar = '\0'; 803113199Stjr if (flags & ALT) 804113199Stjr prec -= expt; 805113199Stjr else 806113199Stjr prec = ndig - expt; 807113199Stjr if (prec < 0) 808113199Stjr prec = 0; 809113723Sdas } else { 810113723Sdas /* 811113723Sdas * Make %[gG] smell like %[eE], but 812113723Sdas * trim trailing zeroes if no # flag. 813113723Sdas */ 814113723Sdas if (!(flags & ALT)) 815113723Sdas prec = ndig; 816113199Stjr } 817103739Stjr } 818113199Stjr if (expchar) { 819113199Stjr expsize = exponent(expstr, expt - 1, expchar); 820113199Stjr size = expsize + prec; 821113199Stjr if (prec > 1 || flags & ALT) 822103739Stjr ++size; 823113199Stjr } else { 824113470Stjr /* space for digits before decimal point */ 825113470Stjr if (expt > 0) 826103739Stjr size = expt; 827113470Stjr else /* "0" */ 828113470Stjr size = 1; 829113470Stjr /* space for decimal pt and following digits */ 830113470Stjr if (prec || flags & ALT) 831113470Stjr size += prec + 1; 832187582Sdas if ((flags & GROUPING) && expt > 0) 833227753Stheraven size += grouping_init(&gs, expt, locale); 834113199Stjr } 835103739Stjr break; 836128821Sdas#endif /* !NO_FLOATING_POINT */ 837103739Stjr case 'n': 838103739Stjr /* 839103739Stjr * Assignment-like behavior is specified if the 840103739Stjr * value overflows or is otherwise unrepresentable. 841103739Stjr * C99 says to use `signed char' for %hhn conversions. 842103739Stjr */ 843103739Stjr if (flags & LLONGINT) 844103739Stjr *GETARG(long long *) = ret; 845103739Stjr else if (flags & SIZET) 846103739Stjr *GETARG(ssize_t *) = (ssize_t)ret; 847103739Stjr else if (flags & PTRDIFFT) 848103739Stjr *GETARG(ptrdiff_t *) = ret; 849103739Stjr else if (flags & INTMAXT) 850103739Stjr *GETARG(intmax_t *) = ret; 851103739Stjr else if (flags & LONGINT) 852103739Stjr *GETARG(long *) = ret; 853103739Stjr else if (flags & SHORTINT) 854103739Stjr *GETARG(short *) = ret; 855103739Stjr else if (flags & CHARINT) 856103739Stjr *GETARG(signed char *) = ret; 857103739Stjr else 858103739Stjr *GETARG(int *) = ret; 859103739Stjr continue; /* no output */ 860103739Stjr case 'O': 861103739Stjr flags |= LONGINT; 862103739Stjr /*FALLTHROUGH*/ 863103739Stjr case 'o': 864103739Stjr if (flags & INTMAX_SIZE) 865103739Stjr ujval = UJARG(); 866103739Stjr else 867103739Stjr ulval = UARG(); 868103739Stjr base = 8; 869103739Stjr goto nosign; 870103739Stjr case 'p': 871103739Stjr /*- 872103739Stjr * ``The argument shall be a pointer to void. The 873103739Stjr * value of the pointer is converted to a sequence 874103739Stjr * of printable characters, in an implementation- 875103739Stjr * defined manner.'' 876103739Stjr * -- ANSI X3J11 877103739Stjr */ 878103739Stjr ujval = (uintmax_t)(uintptr_t)GETARG(void *); 879103739Stjr base = 16; 880113199Stjr xdigs = xdigs_lower; 881113199Stjr flags = flags | INTMAXT; 882113199Stjr ox[1] = 'x'; 883103739Stjr goto nosign; 884105204Stjr case 'S': 885105204Stjr flags |= LONGINT; 886105204Stjr /*FALLTHROUGH*/ 887103739Stjr case 's': 888103739Stjr if (flags & LONGINT) { 889103739Stjr if ((cp = GETARG(wchar_t *)) == NULL) 890103739Stjr cp = L"(null)"; 891103739Stjr } else { 892103739Stjr char *mbp; 893103739Stjr 894103739Stjr if (convbuf != NULL) 895103739Stjr free(convbuf); 896103739Stjr if ((mbp = GETARG(char *)) == NULL) 897103739Stjr cp = L"(null)"; 898103739Stjr else { 899103739Stjr convbuf = __mbsconv(mbp, prec); 900105234Stjr if (convbuf == NULL) { 901105234Stjr fp->_flags |= __SERR; 902103739Stjr goto error; 903105234Stjr } 904103739Stjr cp = convbuf; 905103739Stjr } 906103739Stjr } 907189138Sdas size = (prec >= 0) ? wcsnlen(cp, prec) : wcslen(cp); 908103739Stjr sign = '\0'; 909103739Stjr break; 910103739Stjr case 'U': 911103739Stjr flags |= LONGINT; 912103739Stjr /*FALLTHROUGH*/ 913103739Stjr case 'u': 914103739Stjr if (flags & INTMAX_SIZE) 915103739Stjr ujval = UJARG(); 916103739Stjr else 917103739Stjr ulval = UARG(); 918103739Stjr base = 10; 919103739Stjr goto nosign; 920103739Stjr case 'X': 921113199Stjr xdigs = xdigs_upper; 922103739Stjr goto hex; 923103739Stjr case 'x': 924113199Stjr xdigs = xdigs_lower; 925103739Stjrhex: 926103739Stjr if (flags & INTMAX_SIZE) 927103739Stjr ujval = UJARG(); 928103739Stjr else 929103739Stjr ulval = UARG(); 930103739Stjr base = 16; 931103739Stjr /* leading 0x/X only if non-zero */ 932103739Stjr if (flags & ALT && 933103739Stjr (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) 934113199Stjr ox[1] = ch; 935103739Stjr 936103739Stjr flags &= ~GROUPING; 937103739Stjr /* unsigned conversions */ 938103739Stjrnosign: sign = '\0'; 939103739Stjr /*- 940103739Stjr * ``... diouXx conversions ... if a precision is 941103739Stjr * specified, the 0 flag will be ignored.'' 942103739Stjr * -- ANSI X3J11 943103739Stjr */ 944103739Stjrnumber: if ((dprec = prec) >= 0) 945103739Stjr flags &= ~ZEROPAD; 946103739Stjr 947103739Stjr /*- 948103739Stjr * ``The result of converting a zero value with an 949103739Stjr * explicit precision of zero is no characters.'' 950103739Stjr * -- ANSI X3J11 951145172Sdas * 952145172Sdas * ``The C Standard is clear enough as is. The call 953145172Sdas * printf("%#.0o", 0) should print 0.'' 954145172Sdas * -- Defect Report #151 955103739Stjr */ 956103739Stjr cp = buf + BUF; 957103739Stjr if (flags & INTMAX_SIZE) { 958145172Sdas if (ujval != 0 || prec != 0 || 959145172Sdas (flags & ALT && base == 8)) 960103739Stjr cp = __ujtoa(ujval, cp, base, 961187582Sdas flags & ALT, xdigs); 962103739Stjr } else { 963145172Sdas if (ulval != 0 || prec != 0 || 964145172Sdas (flags & ALT && base == 8)) 965103739Stjr cp = __ultoa(ulval, cp, base, 966187582Sdas flags & ALT, xdigs); 967103739Stjr } 968103739Stjr size = buf + BUF - cp; 969113142Sdas if (size > BUF) /* should never happen */ 970113142Sdas abort(); 971187582Sdas if ((flags & GROUPING) && size != 0) 972227753Stheraven size += grouping_init(&gs, size, locale); 973103739Stjr break; 974103739Stjr default: /* "%?" prints ?, unless ? is NUL */ 975103739Stjr if (ch == '\0') 976103739Stjr goto done; 977103739Stjr /* pretend it was %c with argument ch */ 978103739Stjr cp = buf; 979103739Stjr *cp = ch; 980103739Stjr size = 1; 981103739Stjr sign = '\0'; 982103739Stjr break; 983103739Stjr } 984103739Stjr 985103739Stjr /* 986103739Stjr * All reasonable formats wind up here. At this point, `cp' 987103739Stjr * points to a string which (if not flags&LADJUST) should be 988103739Stjr * padded out to `width' places. If flags&ZEROPAD, it should 989103739Stjr * first be prefixed by any sign or other prefix; otherwise, 990103739Stjr * it should be blank padded before the prefix is emitted. 991103739Stjr * After any left-hand padding and prefixing, emit zeroes 992103739Stjr * required by a decimal [diouxX] precision, then print the 993103739Stjr * string proper, then emit zeroes required by any leftover 994103739Stjr * floating precision; finally, if LADJUST, pad with blanks. 995103739Stjr * 996103739Stjr * Compute actual size, so we know how much to pad. 997103739Stjr * size excludes decimal prec; realsz includes it. 998103739Stjr */ 999103739Stjr realsz = dprec > size ? dprec : size; 1000103739Stjr if (sign) 1001103739Stjr realsz++; 1002124887Sdas if (ox[1]) 1003103739Stjr realsz += 2; 1004103739Stjr 1005103739Stjr prsize = width > realsz ? width : realsz; 1006103739Stjr if ((unsigned)ret + prsize > INT_MAX) { 1007103739Stjr ret = EOF; 1008234531Sdas errno = EOVERFLOW; 1009103739Stjr goto error; 1010103739Stjr } 1011103739Stjr 1012103739Stjr /* right-adjusting blank padding */ 1013103739Stjr if ((flags & (LADJUST|ZEROPAD)) == 0) 1014103739Stjr PAD(width - realsz, blanks); 1015103739Stjr 1016103739Stjr /* prefix */ 1017124887Sdas if (sign) 1018103739Stjr PRINT(&sign, 1); 1019124887Sdas 1020124887Sdas if (ox[1]) { /* ox[1] is either x, X, or \0 */ 1021103739Stjr ox[0] = '0'; 1022103739Stjr PRINT(ox, 2); 1023103739Stjr } 1024103739Stjr 1025103739Stjr /* right-adjusting zero padding */ 1026103739Stjr if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 1027103739Stjr PAD(width - realsz, zeroes); 1028103739Stjr 1029103739Stjr /* the string or number proper */ 1030128821Sdas#ifndef NO_FLOATING_POINT 1031103739Stjr if ((flags & FPT) == 0) { 1032187582Sdas#endif 1033187582Sdas /* leading zeroes from decimal precision */ 1034187582Sdas PAD(dprec - size, zeroes); 1035187582Sdas if (gs.grouping) { 1036227753Stheraven if (grouping_print(&gs, &io, cp, buf+BUF, locale) < 0) 1037187582Sdas goto error; 1038187582Sdas } else { 1039187582Sdas PRINT(cp, size); 1040187582Sdas } 1041187582Sdas#ifndef NO_FLOATING_POINT 1042103739Stjr } else { /* glue together f_p fragments */ 1043113199Stjr if (!expchar) { /* %[fF] or sufficiently short %[gG] */ 1044113199Stjr if (expt <= 0) { 1045113470Stjr PRINT(zeroes, 1); 1046187421Sdas if (prec || flags & ALT) 1047187421Sdas PRINT(&decimal_point, 1); 1048103739Stjr PAD(-expt, zeroes); 1049113199Stjr /* already handled initial 0's */ 1050113199Stjr prec += expt; 1051103739Stjr } else { 1052187582Sdas if (gs.grouping) { 1053187582Sdas n = grouping_print(&gs, &io, 1054227753Stheraven cp, convbuf + ndig, locale); 1055187582Sdas if (n < 0) 1056187582Sdas goto error; 1057187582Sdas cp += n; 1058187582Sdas } else { 1059187582Sdas PRINTANDPAD(cp, convbuf + ndig, 1060187582Sdas expt, zeroes); 1061187582Sdas cp += expt; 1062113199Stjr } 1063187421Sdas if (prec || flags & ALT) 1064187421Sdas PRINT(&decimal_point, 1); 1065103739Stjr } 1066113199Stjr PRINTANDPAD(cp, convbuf + ndig, prec, zeroes); 1067113199Stjr } else { /* %[eE] or sufficiently long %[gG] */ 1068113199Stjr if (prec > 1 || flags & ALT) { 1069113199Stjr buf[0] = *cp++; 1070187421Sdas buf[1] = decimal_point; 1071113199Stjr PRINT(buf, 2); 1072113199Stjr PRINT(cp, ndig-1); 1073113199Stjr PAD(prec - ndig, zeroes); 1074103739Stjr } else /* XeYYY */ 1075103739Stjr PRINT(cp, 1); 1076103739Stjr PRINT(expstr, expsize); 1077103739Stjr } 1078103739Stjr } 1079103739Stjr#endif 1080103739Stjr /* left-adjusting padding (always blank) */ 1081103739Stjr if (flags & LADJUST) 1082103739Stjr PAD(width - realsz, blanks); 1083103739Stjr 1084103739Stjr /* finally, adjust ret */ 1085103739Stjr ret += prsize; 1086187277Sdas 1087187277Sdas FLUSH(); /* copy out the I/O vectors */ 1088103739Stjr } 1089103739Stjrdone: 1090187277Sdas FLUSH(); 1091103739Stjrerror: 1092134332Sdes va_end(orgap); 1093103739Stjr if (convbuf != NULL) 1094103739Stjr free(convbuf); 1095103739Stjr if (__sferror(fp)) 1096103739Stjr ret = EOF; 1097103739Stjr if ((argtable != NULL) && (argtable != statargtable)) 1098103739Stjr free (argtable); 1099103739Stjr return (ret); 1100103739Stjr /* NOTREACHED */ 1101103739Stjr} 1102