vfprintf.c revision 31871
11573Srgrimes/*- 21573Srgrimes * Copyright (c) 1990, 1993 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * This code is derived from software contributed to Berkeley by 61573Srgrimes * Chris Torek. 71573Srgrimes * 81573Srgrimes * Redistribution and use in source and binary forms, with or without 91573Srgrimes * modification, are permitted provided that the following conditions 101573Srgrimes * are met: 111573Srgrimes * 1. Redistributions of source code must retain the above copyright 121573Srgrimes * notice, this list of conditions and the following disclaimer. 131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141573Srgrimes * notice, this list of conditions and the following disclaimer in the 151573Srgrimes * documentation and/or other materials provided with the distribution. 161573Srgrimes * 3. All advertising materials mentioning features or use of this software 171573Srgrimes * must display the following acknowledgement: 181573Srgrimes * This product includes software developed by the University of 191573Srgrimes * California, Berkeley and its contributors. 201573Srgrimes * 4. Neither the name of the University nor the names of its contributors 211573Srgrimes * may be used to endorse or promote products derived from this software 221573Srgrimes * without specific prior written permission. 231573Srgrimes * 241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341573Srgrimes * SUCH DAMAGE. 351573Srgrimes */ 361573Srgrimes 371573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 3816586Sjraynard#if 0 391573Srgrimesstatic char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93"; 4016586Sjraynard#endif 4116586Sjraynardstatic const char rcsid[] = 4231871Sbde "$Id: vfprintf.c,v 1.12 1997/02/22 15:02:40 peter Exp $"; 431573Srgrimes#endif /* LIBC_SCCS and not lint */ 441573Srgrimes 451573Srgrimes/* 461573Srgrimes * Actual printf innards. 471573Srgrimes * 481573Srgrimes * This code is large and complicated... 491573Srgrimes */ 501573Srgrimes 511573Srgrimes#include <sys/types.h> 521573Srgrimes 531573Srgrimes#include <limits.h> 541573Srgrimes#include <stdio.h> 551573Srgrimes#include <stdlib.h> 561573Srgrimes#include <string.h> 571573Srgrimes 581573Srgrimes#if __STDC__ 591573Srgrimes#include <stdarg.h> 601573Srgrimes#else 611573Srgrimes#include <varargs.h> 621573Srgrimes#endif 631573Srgrimes 641573Srgrimes#include "local.h" 651573Srgrimes#include "fvwrite.h" 6613545Sjulian#ifdef _THREAD_SAFE 6713545Sjulian#include <pthread.h> 6813545Sjulian#include "pthread_private.h" 6913545Sjulian#endif 701573Srgrimes 711573Srgrimes/* Define FLOATING_POINT to get floating point. */ 721573Srgrimes#define FLOATING_POINT 731573Srgrimes 7416586Sjraynardstatic int __sprint __P((FILE *, struct __suio *)); 7516586Sjraynardstatic int __sbprintf __P((FILE *, const char *, va_list)); 7616586Sjraynardstatic char * __ultoa __P((u_long, char *, int, int, char *)); 7716586Sjraynardstatic char * __uqtoa __P((u_quad_t, char *, int, int, char *)); 7821674Sjkhstatic void __find_arguments __P((const char *, va_list, void ***)); 7921674Sjkhstatic void __grow_type_table __P((int, unsigned char **, int *)); 8016586Sjraynard 811573Srgrimes/* 821573Srgrimes * Flush out all the vectors defined by the given uio, 831573Srgrimes * then reset it so that it can be reused. 841573Srgrimes */ 851573Srgrimesstatic int 861573Srgrimes__sprint(fp, uio) 871573Srgrimes FILE *fp; 881573Srgrimes register struct __suio *uio; 891573Srgrimes{ 901573Srgrimes register int err; 911573Srgrimes 921573Srgrimes if (uio->uio_resid == 0) { 931573Srgrimes uio->uio_iovcnt = 0; 941573Srgrimes return (0); 951573Srgrimes } 961573Srgrimes err = __sfvwrite(fp, uio); 971573Srgrimes uio->uio_resid = 0; 981573Srgrimes uio->uio_iovcnt = 0; 991573Srgrimes return (err); 1001573Srgrimes} 1011573Srgrimes 1021573Srgrimes/* 1031573Srgrimes * Helper function for `fprintf to unbuffered unix file': creates a 1041573Srgrimes * temporary buffer. We only work on write-only files; this avoids 1051573Srgrimes * worries about ungetc buffers and so forth. 1061573Srgrimes */ 1071573Srgrimesstatic int 1081573Srgrimes__sbprintf(fp, fmt, ap) 1091573Srgrimes register FILE *fp; 1101573Srgrimes const char *fmt; 1111573Srgrimes va_list ap; 1121573Srgrimes{ 1131573Srgrimes int ret; 1141573Srgrimes FILE fake; 1151573Srgrimes unsigned char buf[BUFSIZ]; 1161573Srgrimes 1171573Srgrimes /* copy the important variables */ 1181573Srgrimes fake._flags = fp->_flags & ~__SNBF; 1191573Srgrimes fake._file = fp->_file; 1201573Srgrimes fake._cookie = fp->_cookie; 1211573Srgrimes fake._write = fp->_write; 1221573Srgrimes 1231573Srgrimes /* set up the buffer */ 1241573Srgrimes fake._bf._base = fake._p = buf; 1251573Srgrimes fake._bf._size = fake._w = sizeof(buf); 1261573Srgrimes fake._lbfsize = 0; /* not actually used, but Just In Case */ 1271573Srgrimes 1281573Srgrimes /* do the work, then copy any error status */ 1291573Srgrimes ret = vfprintf(&fake, fmt, ap); 1301573Srgrimes if (ret >= 0 && fflush(&fake)) 1311573Srgrimes ret = EOF; 1321573Srgrimes if (fake._flags & __SERR) 1331573Srgrimes fp->_flags |= __SERR; 1341573Srgrimes return (ret); 1351573Srgrimes} 1361573Srgrimes 1371573Srgrimes/* 1381573Srgrimes * Macros for converting digits to letters and vice versa 1391573Srgrimes */ 1401573Srgrimes#define to_digit(c) ((c) - '0') 1411573Srgrimes#define is_digit(c) ((unsigned)to_digit(c) <= 9) 1421573Srgrimes#define to_char(n) ((n) + '0') 1431573Srgrimes 1441573Srgrimes/* 1451573Srgrimes * Convert an unsigned long to ASCII for printf purposes, returning 1461573Srgrimes * a pointer to the first character of the string representation. 1471573Srgrimes * Octal numbers can be forced to have a leading zero; hex numbers 1481573Srgrimes * use the given digits. 1491573Srgrimes */ 1501573Srgrimesstatic char * 1511573Srgrimes__ultoa(val, endp, base, octzero, xdigs) 1521573Srgrimes register u_long val; 1531573Srgrimes char *endp; 1541573Srgrimes int base, octzero; 1551573Srgrimes char *xdigs; 1561573Srgrimes{ 1571573Srgrimes register char *cp = endp; 1581573Srgrimes register long sval; 1591573Srgrimes 1601573Srgrimes /* 1611573Srgrimes * Handle the three cases separately, in the hope of getting 1621573Srgrimes * better/faster code. 1631573Srgrimes */ 1641573Srgrimes switch (base) { 1651573Srgrimes case 10: 1661573Srgrimes if (val < 10) { /* many numbers are 1 digit */ 1671573Srgrimes *--cp = to_char(val); 1681573Srgrimes return (cp); 1691573Srgrimes } 1701573Srgrimes /* 1711573Srgrimes * On many machines, unsigned arithmetic is harder than 1721573Srgrimes * signed arithmetic, so we do at most one unsigned mod and 1731573Srgrimes * divide; this is sufficient to reduce the range of 1741573Srgrimes * the incoming value to where signed arithmetic works. 1751573Srgrimes */ 1761573Srgrimes if (val > LONG_MAX) { 1771573Srgrimes *--cp = to_char(val % 10); 1781573Srgrimes sval = val / 10; 1791573Srgrimes } else 1801573Srgrimes sval = val; 1811573Srgrimes do { 1821573Srgrimes *--cp = to_char(sval % 10); 1831573Srgrimes sval /= 10; 1841573Srgrimes } while (sval != 0); 1851573Srgrimes break; 1861573Srgrimes 1871573Srgrimes case 8: 1881573Srgrimes do { 1891573Srgrimes *--cp = to_char(val & 7); 1901573Srgrimes val >>= 3; 1911573Srgrimes } while (val); 1921573Srgrimes if (octzero && *cp != '0') 1931573Srgrimes *--cp = '0'; 1941573Srgrimes break; 1951573Srgrimes 1961573Srgrimes case 16: 1971573Srgrimes do { 1981573Srgrimes *--cp = xdigs[val & 15]; 1991573Srgrimes val >>= 4; 2001573Srgrimes } while (val); 2011573Srgrimes break; 2021573Srgrimes 2031573Srgrimes default: /* oops */ 2041573Srgrimes abort(); 2051573Srgrimes } 2061573Srgrimes return (cp); 2071573Srgrimes} 2081573Srgrimes 2091573Srgrimes/* Identical to __ultoa, but for quads. */ 2101573Srgrimesstatic char * 2111573Srgrimes__uqtoa(val, endp, base, octzero, xdigs) 2121573Srgrimes register u_quad_t val; 2131573Srgrimes char *endp; 2141573Srgrimes int base, octzero; 2151573Srgrimes char *xdigs; 2161573Srgrimes{ 2171573Srgrimes register char *cp = endp; 2181573Srgrimes register quad_t sval; 2191573Srgrimes 2201573Srgrimes /* quick test for small values; __ultoa is typically much faster */ 2211573Srgrimes /* (perhaps instead we should run until small, then call __ultoa?) */ 2221573Srgrimes if (val <= ULONG_MAX) 2231573Srgrimes return (__ultoa((u_long)val, endp, base, octzero, xdigs)); 2241573Srgrimes switch (base) { 2251573Srgrimes case 10: 2261573Srgrimes if (val < 10) { 2271573Srgrimes *--cp = to_char(val % 10); 2281573Srgrimes return (cp); 2291573Srgrimes } 2301573Srgrimes if (val > QUAD_MAX) { 2311573Srgrimes *--cp = to_char(val % 10); 2321573Srgrimes sval = val / 10; 2331573Srgrimes } else 2341573Srgrimes sval = val; 2351573Srgrimes do { 2361573Srgrimes *--cp = to_char(sval % 10); 2371573Srgrimes sval /= 10; 2381573Srgrimes } while (sval != 0); 2391573Srgrimes break; 2401573Srgrimes 2411573Srgrimes case 8: 2421573Srgrimes do { 2431573Srgrimes *--cp = to_char(val & 7); 2441573Srgrimes val >>= 3; 2451573Srgrimes } while (val); 2461573Srgrimes if (octzero && *cp != '0') 2471573Srgrimes *--cp = '0'; 2481573Srgrimes break; 2491573Srgrimes 2501573Srgrimes case 16: 2511573Srgrimes do { 2521573Srgrimes *--cp = xdigs[val & 15]; 2531573Srgrimes val >>= 4; 2541573Srgrimes } while (val); 2551573Srgrimes break; 2561573Srgrimes 2571573Srgrimes default: 2581573Srgrimes abort(); 2591573Srgrimes } 2601573Srgrimes return (cp); 2611573Srgrimes} 2621573Srgrimes 2631573Srgrimes#ifdef FLOATING_POINT 2641573Srgrimes#include <math.h> 2651573Srgrimes#include "floatio.h" 2661573Srgrimes 2671573Srgrimes#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 2681573Srgrimes#define DEFPREC 6 2691573Srgrimes 2701573Srgrimesstatic char *cvt __P((double, int, int, char *, int *, int, int *)); 2711573Srgrimesstatic int exponent __P((char *, int, int)); 2721573Srgrimes 2731573Srgrimes#else /* no FLOATING_POINT */ 2741573Srgrimes 2751573Srgrimes#define BUF 68 2761573Srgrimes 2771573Srgrimes#endif /* FLOATING_POINT */ 2781573Srgrimes 27921674Sjkh#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ 2801573Srgrimes 2811573Srgrimes/* 2821573Srgrimes * Flags used during conversion. 2831573Srgrimes */ 2841573Srgrimes#define ALT 0x001 /* alternate form */ 2851573Srgrimes#define HEXPREFIX 0x002 /* add 0x or 0X prefix */ 2861573Srgrimes#define LADJUST 0x004 /* left adjustment */ 28731871Sbde#define LONGDBL 0x008 /* long double */ 2881573Srgrimes#define LONGINT 0x010 /* long integer */ 2891573Srgrimes#define QUADINT 0x020 /* quad integer */ 2901573Srgrimes#define SHORTINT 0x040 /* short integer */ 2911573Srgrimes#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ 2921573Srgrimes#define FPT 0x100 /* Floating point number */ 2931573Srgrimesint 2941573Srgrimesvfprintf(fp, fmt0, ap) 2951573Srgrimes FILE *fp; 2961573Srgrimes const char *fmt0; 2971573Srgrimes va_list ap; 2981573Srgrimes{ 2991573Srgrimes register char *fmt; /* format string */ 3001573Srgrimes register int ch; /* character from fmt */ 30121674Sjkh register int n, n2; /* handy integer (short term usage) */ 3021573Srgrimes register char *cp; /* handy char pointer (short term usage) */ 3031573Srgrimes register struct __siov *iovp;/* for PRINT macro */ 3041573Srgrimes register int flags; /* flags as above */ 3051573Srgrimes int ret; /* return value accumulator */ 3061573Srgrimes int width; /* width from format (%8d), or 0 */ 3071573Srgrimes int prec; /* precision from format (%.3d), or -1 */ 3081573Srgrimes char sign; /* sign prefix (' ', '+', '-', or \0) */ 3091573Srgrimes#ifdef FLOATING_POINT 3101573Srgrimes char softsign; /* temporary negative sign for floats */ 3111573Srgrimes double _double; /* double precision arguments %[eEfgG] */ 3121573Srgrimes int expt; /* integer value of exponent */ 3131573Srgrimes int expsize; /* character count for expstr */ 3141573Srgrimes int ndig; /* actual number of digits returned by cvt */ 3151573Srgrimes char expstr[7]; /* buffer for exponent string */ 3161573Srgrimes#endif 3171573Srgrimes u_long ulval; /* integer arguments %[diouxX] */ 3181573Srgrimes u_quad_t uqval; /* %q integers */ 3191573Srgrimes int base; /* base for [diouxX] conversion */ 3201573Srgrimes int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 32114727Sfenner int realsz; /* field size expanded by dprec, sign, etc */ 3221573Srgrimes int size; /* size of converted field or string */ 3231573Srgrimes char *xdigs; /* digits for [xX] conversion */ 3241573Srgrimes#define NIOV 8 3251573Srgrimes struct __suio uio; /* output information: summary */ 3261573Srgrimes struct __siov iov[NIOV];/* ... and individual io vectors */ 3271573Srgrimes char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 3281573Srgrimes char ox[2]; /* space for 0x hex-prefix */ 32921674Sjkh void **argtable; /* args, built due to positional arg */ 33021674Sjkh void *statargtable [STATIC_ARG_TBL_SIZE]; 33121674Sjkh int nextarg; /* 1-based argument index */ 33221674Sjkh va_list orgap; /* original argument pointer */ 3331573Srgrimes 3341573Srgrimes /* 3351573Srgrimes * Choose PADSIZE to trade efficiency vs. size. If larger printf 3361573Srgrimes * fields occur frequently, increase PADSIZE and make the initialisers 3371573Srgrimes * below longer. 3381573Srgrimes */ 3391573Srgrimes#define PADSIZE 16 /* pad chunk size */ 3401573Srgrimes static char blanks[PADSIZE] = 3411573Srgrimes {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 3421573Srgrimes static char zeroes[PADSIZE] = 3431573Srgrimes {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 3441573Srgrimes 3451573Srgrimes /* 3461573Srgrimes * BEWARE, these `goto error' on error, and PAD uses `n'. 3471573Srgrimes */ 3481573Srgrimes#define PRINT(ptr, len) { \ 3491573Srgrimes iovp->iov_base = (ptr); \ 3501573Srgrimes iovp->iov_len = (len); \ 3511573Srgrimes uio.uio_resid += (len); \ 3521573Srgrimes iovp++; \ 3531573Srgrimes if (++uio.uio_iovcnt >= NIOV) { \ 3541573Srgrimes if (__sprint(fp, &uio)) \ 3551573Srgrimes goto error; \ 3561573Srgrimes iovp = iov; \ 3571573Srgrimes } \ 3581573Srgrimes} 3591573Srgrimes#define PAD(howmany, with) { \ 3601573Srgrimes if ((n = (howmany)) > 0) { \ 3611573Srgrimes while (n > PADSIZE) { \ 3621573Srgrimes PRINT(with, PADSIZE); \ 3631573Srgrimes n -= PADSIZE; \ 3641573Srgrimes } \ 3651573Srgrimes PRINT(with, n); \ 3661573Srgrimes } \ 3671573Srgrimes} 3681573Srgrimes#define FLUSH() { \ 3691573Srgrimes if (uio.uio_resid && __sprint(fp, &uio)) \ 3701573Srgrimes goto error; \ 3711573Srgrimes uio.uio_iovcnt = 0; \ 3721573Srgrimes iovp = iov; \ 3731573Srgrimes} 3741573Srgrimes 37521674Sjkh /* 37621674Sjkh * Get the argument indexed by nextarg. If the argument table is 37721674Sjkh * built, use it to get the argument. If its not, get the next 37821674Sjkh * argument (and arguments must be gotten sequentially). 37921674Sjkh */ 38021674Sjkh#define GETARG(type) \ 38121674Sjkh ((argtable != NULL) ? *((type*)(argtable[nextarg++])) : \ 38221674Sjkh (nextarg++, va_arg(ap, type))) 38321674Sjkh 3841573Srgrimes /* 3851573Srgrimes * To extend shorts properly, we need both signed and unsigned 3861573Srgrimes * argument extraction methods. 3871573Srgrimes */ 3881573Srgrimes#define SARG() \ 38921674Sjkh (flags&LONGINT ? GETARG(long) : \ 39021674Sjkh flags&SHORTINT ? (long)(short)GETARG(int) : \ 39121674Sjkh (long)GETARG(int)) 3921573Srgrimes#define UARG() \ 39321674Sjkh (flags&LONGINT ? GETARG(u_long) : \ 39421674Sjkh flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 39521674Sjkh (u_long)GETARG(u_int)) 3961573Srgrimes 39721674Sjkh /* 39821674Sjkh * Get * arguments, including the form *nn$. Preserve the nextarg 39921674Sjkh * that the argument can be gotten once the type is determined. 40021674Sjkh */ 40121674Sjkh#define GETASTER(val) \ 40221674Sjkh n2 = 0; \ 40321674Sjkh cp = fmt; \ 40421674Sjkh while (is_digit(*cp)) { \ 40521674Sjkh n2 = 10 * n2 + to_digit(*cp); \ 40621674Sjkh cp++; \ 40721674Sjkh } \ 40821674Sjkh if (*cp == '$') { \ 40921674Sjkh int hold = nextarg; \ 41021674Sjkh if (argtable == NULL) { \ 41121674Sjkh argtable = statargtable; \ 41221674Sjkh __find_arguments (fmt0, orgap, &argtable); \ 41321674Sjkh } \ 41421674Sjkh nextarg = n2; \ 41521674Sjkh val = GETARG (int); \ 41621674Sjkh nextarg = hold; \ 41721674Sjkh fmt = ++cp; \ 41821674Sjkh } else { \ 41921674Sjkh val = GETARG (int); \ 42021674Sjkh } 42121674Sjkh 42221674Sjkh 42313545Sjulian#ifdef _THREAD_SAFE 42413545Sjulian _thread_flockfile(fp,__FILE__,__LINE__); 42513545Sjulian#endif 4261573Srgrimes /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 42713545Sjulian if (cantwrite(fp)) { 42813545Sjulian#ifdef _THREAD_SAFE 42913545Sjulian _thread_funlockfile(fp); 43013545Sjulian#endif 4311573Srgrimes return (EOF); 43213545Sjulian } 4331573Srgrimes 4341573Srgrimes /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 4351573Srgrimes if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 43613545Sjulian fp->_file >= 0) { 43713545Sjulian#ifdef _THREAD_SAFE 43813545Sjulian _thread_funlockfile(fp); 43913545Sjulian#endif 4401573Srgrimes return (__sbprintf(fp, fmt0, ap)); 44113545Sjulian } 4421573Srgrimes 4431573Srgrimes fmt = (char *)fmt0; 44421674Sjkh argtable = NULL; 44521674Sjkh nextarg = 1; 44621674Sjkh orgap = ap; 4471573Srgrimes uio.uio_iov = iovp = iov; 4481573Srgrimes uio.uio_resid = 0; 4491573Srgrimes uio.uio_iovcnt = 0; 4501573Srgrimes ret = 0; 4511573Srgrimes 4521573Srgrimes /* 4531573Srgrimes * Scan the format for conversions (`%' character). 4541573Srgrimes */ 4551573Srgrimes for (;;) { 4561573Srgrimes for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 4571573Srgrimes /* void */; 4581573Srgrimes if ((n = fmt - cp) != 0) { 4591573Srgrimes PRINT(cp, n); 4601573Srgrimes ret += n; 4611573Srgrimes } 4621573Srgrimes if (ch == '\0') 4631573Srgrimes goto done; 4641573Srgrimes fmt++; /* skip over '%' */ 4651573Srgrimes 4661573Srgrimes flags = 0; 4671573Srgrimes dprec = 0; 4681573Srgrimes width = 0; 4691573Srgrimes prec = -1; 4701573Srgrimes sign = '\0'; 4711573Srgrimes 4721573Srgrimesrflag: ch = *fmt++; 4731573Srgrimesreswitch: switch (ch) { 4741573Srgrimes case ' ': 4751573Srgrimes /* 4761573Srgrimes * ``If the space and + flags both appear, the space 4771573Srgrimes * flag will be ignored.'' 4781573Srgrimes * -- ANSI X3J11 4791573Srgrimes */ 4801573Srgrimes if (!sign) 4811573Srgrimes sign = ' '; 4821573Srgrimes goto rflag; 4831573Srgrimes case '#': 4841573Srgrimes flags |= ALT; 4851573Srgrimes goto rflag; 4861573Srgrimes case '*': 4871573Srgrimes /* 4881573Srgrimes * ``A negative field width argument is taken as a 4891573Srgrimes * - flag followed by a positive field width.'' 4901573Srgrimes * -- ANSI X3J11 4911573Srgrimes * They don't exclude field widths read from args. 4921573Srgrimes */ 49321674Sjkh GETASTER (width); 49421674Sjkh if (width >= 0) 4951573Srgrimes goto rflag; 4961573Srgrimes width = -width; 4971573Srgrimes /* FALLTHROUGH */ 4981573Srgrimes case '-': 4991573Srgrimes flags |= LADJUST; 5001573Srgrimes goto rflag; 5011573Srgrimes case '+': 5021573Srgrimes sign = '+'; 5031573Srgrimes goto rflag; 5041573Srgrimes case '.': 5051573Srgrimes if ((ch = *fmt++) == '*') { 50621674Sjkh GETASTER (n); 5071573Srgrimes prec = n < 0 ? -1 : n; 5081573Srgrimes goto rflag; 5091573Srgrimes } 5101573Srgrimes n = 0; 5111573Srgrimes while (is_digit(ch)) { 5121573Srgrimes n = 10 * n + to_digit(ch); 5131573Srgrimes ch = *fmt++; 5141573Srgrimes } 5151573Srgrimes prec = n < 0 ? -1 : n; 5161573Srgrimes goto reswitch; 5171573Srgrimes case '0': 5181573Srgrimes /* 5191573Srgrimes * ``Note that 0 is taken as a flag, not as the 5201573Srgrimes * beginning of a field width.'' 5211573Srgrimes * -- ANSI X3J11 5221573Srgrimes */ 5231573Srgrimes flags |= ZEROPAD; 5241573Srgrimes goto rflag; 5251573Srgrimes case '1': case '2': case '3': case '4': 5261573Srgrimes case '5': case '6': case '7': case '8': case '9': 5271573Srgrimes n = 0; 5281573Srgrimes do { 5291573Srgrimes n = 10 * n + to_digit(ch); 5301573Srgrimes ch = *fmt++; 5311573Srgrimes } while (is_digit(ch)); 53221674Sjkh if (ch == '$') { 53321674Sjkh nextarg = n; 53421674Sjkh if (argtable == NULL) { 53521674Sjkh argtable = statargtable; 53621674Sjkh __find_arguments (fmt0, orgap, 53721674Sjkh &argtable); 53821674Sjkh } 53921674Sjkh goto rflag; 54021674Sjkh } 5411573Srgrimes width = n; 5421573Srgrimes goto reswitch; 5431573Srgrimes#ifdef FLOATING_POINT 5441573Srgrimes case 'L': 5451573Srgrimes flags |= LONGDBL; 5461573Srgrimes goto rflag; 5471573Srgrimes#endif 5481573Srgrimes case 'h': 5491573Srgrimes flags |= SHORTINT; 5501573Srgrimes goto rflag; 5511573Srgrimes case 'l': 5521573Srgrimes flags |= LONGINT; 5531573Srgrimes goto rflag; 5541573Srgrimes case 'q': 5551573Srgrimes flags |= QUADINT; 5561573Srgrimes goto rflag; 5571573Srgrimes case 'c': 55821674Sjkh *(cp = buf) = GETARG(int); 5591573Srgrimes size = 1; 5601573Srgrimes sign = '\0'; 5611573Srgrimes break; 5621573Srgrimes case 'D': 5631573Srgrimes flags |= LONGINT; 5641573Srgrimes /*FALLTHROUGH*/ 5651573Srgrimes case 'd': 5661573Srgrimes case 'i': 5671573Srgrimes if (flags & QUADINT) { 56821674Sjkh uqval = GETARG(quad_t); 5691573Srgrimes if ((quad_t)uqval < 0) { 5701573Srgrimes uqval = -uqval; 5711573Srgrimes sign = '-'; 5721573Srgrimes } 5731573Srgrimes } else { 5741573Srgrimes ulval = SARG(); 5751573Srgrimes if ((long)ulval < 0) { 5761573Srgrimes ulval = -ulval; 5771573Srgrimes sign = '-'; 5781573Srgrimes } 5791573Srgrimes } 5801573Srgrimes base = 10; 5811573Srgrimes goto number; 5821573Srgrimes#ifdef FLOATING_POINT 5837033Sbde case 'e': 5841573Srgrimes case 'E': 5857033Sbde case 'f': 5867033Sbde goto fp_begin; 5871573Srgrimes case 'g': 5881573Srgrimes case 'G': 5897033Sbde if (prec == 0) 5907033Sbde prec = 1; 5917033Sbdefp_begin: if (prec == -1) 5921573Srgrimes prec = DEFPREC; 5937033Sbde if (flags & LONGDBL) 59431871Sbde /* XXX this loses precision. */ 59521674Sjkh _double = (double)GETARG(long double); 5967033Sbde else 59721674Sjkh _double = GETARG(double); 5981573Srgrimes /* do this before tricky precision changes */ 5991573Srgrimes if (isinf(_double)) { 6001573Srgrimes if (_double < 0) 6011573Srgrimes sign = '-'; 6021573Srgrimes cp = "Inf"; 6031573Srgrimes size = 3; 6041573Srgrimes break; 6051573Srgrimes } 6061573Srgrimes if (isnan(_double)) { 6071573Srgrimes cp = "NaN"; 6081573Srgrimes size = 3; 6091573Srgrimes break; 6101573Srgrimes } 6111573Srgrimes flags |= FPT; 6121573Srgrimes cp = cvt(_double, prec, flags, &softsign, 6131573Srgrimes &expt, ch, &ndig); 6141573Srgrimes if (ch == 'g' || ch == 'G') { 6151573Srgrimes if (expt <= -4 || expt > prec) 6161573Srgrimes ch = (ch == 'g') ? 'e' : 'E'; 6171573Srgrimes else 6181573Srgrimes ch = 'g'; 6198870Srgrimes } 6201573Srgrimes if (ch <= 'e') { /* 'e' or 'E' fmt */ 6211573Srgrimes --expt; 6221573Srgrimes expsize = exponent(expstr, expt, ch); 6231573Srgrimes size = expsize + ndig; 6241573Srgrimes if (ndig > 1 || flags & ALT) 6251573Srgrimes ++size; 6261573Srgrimes } else if (ch == 'f') { /* f fmt */ 6271573Srgrimes if (expt > 0) { 6281573Srgrimes size = expt; 6291573Srgrimes if (prec || flags & ALT) 6301573Srgrimes size += prec + 1; 6311573Srgrimes } else /* "0.X" */ 6321573Srgrimes size = prec + 2; 6331573Srgrimes } else if (expt >= ndig) { /* fixed g fmt */ 6341573Srgrimes size = expt; 6351573Srgrimes if (flags & ALT) 6361573Srgrimes ++size; 6371573Srgrimes } else 6381573Srgrimes size = ndig + (expt > 0 ? 6391573Srgrimes 1 : 2 - expt); 6401573Srgrimes 6411573Srgrimes if (softsign) 6421573Srgrimes sign = '-'; 6431573Srgrimes break; 6441573Srgrimes#endif /* FLOATING_POINT */ 6451573Srgrimes case 'n': 6461573Srgrimes if (flags & QUADINT) 64721674Sjkh *GETARG(quad_t *) = ret; 6481573Srgrimes else if (flags & LONGINT) 64921674Sjkh *GETARG(long *) = ret; 6501573Srgrimes else if (flags & SHORTINT) 65121674Sjkh *GETARG(short *) = ret; 6521573Srgrimes else 65321674Sjkh *GETARG(int *) = ret; 6541573Srgrimes continue; /* no output */ 6551573Srgrimes case 'O': 6561573Srgrimes flags |= LONGINT; 6571573Srgrimes /*FALLTHROUGH*/ 6581573Srgrimes case 'o': 6591573Srgrimes if (flags & QUADINT) 66021674Sjkh uqval = GETARG(u_quad_t); 6611573Srgrimes else 6621573Srgrimes ulval = UARG(); 6631573Srgrimes base = 8; 6641573Srgrimes goto nosign; 6651573Srgrimes case 'p': 6661573Srgrimes /* 6671573Srgrimes * ``The argument shall be a pointer to void. The 6681573Srgrimes * value of the pointer is converted to a sequence 6691573Srgrimes * of printable characters, in an implementation- 6701573Srgrimes * defined manner.'' 6711573Srgrimes * -- ANSI X3J11 6721573Srgrimes */ 67321674Sjkh ulval = (u_long)GETARG(void *); 6741573Srgrimes base = 16; 6751573Srgrimes xdigs = "0123456789abcdef"; 6761573Srgrimes flags = (flags & ~QUADINT) | HEXPREFIX; 6771573Srgrimes ch = 'x'; 6781573Srgrimes goto nosign; 6791573Srgrimes case 's': 68021674Sjkh if ((cp = GETARG(char *)) == NULL) 6811573Srgrimes cp = "(null)"; 6821573Srgrimes if (prec >= 0) { 6831573Srgrimes /* 6841573Srgrimes * can't use strlen; can only look for the 6851573Srgrimes * NUL in the first `prec' characters, and 6861573Srgrimes * strlen() will go further. 6871573Srgrimes */ 68816586Sjraynard char *p = memchr(cp, 0, (size_t)prec); 6891573Srgrimes 6901573Srgrimes if (p != NULL) { 6911573Srgrimes size = p - cp; 6921573Srgrimes if (size > prec) 6931573Srgrimes size = prec; 6941573Srgrimes } else 6951573Srgrimes size = prec; 6961573Srgrimes } else 6971573Srgrimes size = strlen(cp); 6981573Srgrimes sign = '\0'; 6991573Srgrimes break; 7001573Srgrimes case 'U': 7011573Srgrimes flags |= LONGINT; 7021573Srgrimes /*FALLTHROUGH*/ 7031573Srgrimes case 'u': 7041573Srgrimes if (flags & QUADINT) 70521674Sjkh uqval = GETARG(u_quad_t); 7061573Srgrimes else 7071573Srgrimes ulval = UARG(); 7081573Srgrimes base = 10; 7091573Srgrimes goto nosign; 7101573Srgrimes case 'X': 7111573Srgrimes xdigs = "0123456789ABCDEF"; 7121573Srgrimes goto hex; 7131573Srgrimes case 'x': 7141573Srgrimes xdigs = "0123456789abcdef"; 7151573Srgrimeshex: if (flags & QUADINT) 71621674Sjkh uqval = GETARG(u_quad_t); 7171573Srgrimes else 7181573Srgrimes ulval = UARG(); 7191573Srgrimes base = 16; 7201573Srgrimes /* leading 0x/X only if non-zero */ 7211573Srgrimes if (flags & ALT && 7221573Srgrimes (flags & QUADINT ? uqval != 0 : ulval != 0)) 7231573Srgrimes flags |= HEXPREFIX; 7241573Srgrimes 7251573Srgrimes /* unsigned conversions */ 7261573Srgrimesnosign: sign = '\0'; 7271573Srgrimes /* 7281573Srgrimes * ``... diouXx conversions ... if a precision is 7291573Srgrimes * specified, the 0 flag will be ignored.'' 7301573Srgrimes * -- ANSI X3J11 7311573Srgrimes */ 7321573Srgrimesnumber: if ((dprec = prec) >= 0) 7331573Srgrimes flags &= ~ZEROPAD; 7341573Srgrimes 7351573Srgrimes /* 7361573Srgrimes * ``The result of converting a zero value with an 7371573Srgrimes * explicit precision of zero is no characters.'' 7381573Srgrimes * -- ANSI X3J11 7391573Srgrimes */ 7401573Srgrimes cp = buf + BUF; 7411573Srgrimes if (flags & QUADINT) { 7421573Srgrimes if (uqval != 0 || prec != 0) 7431573Srgrimes cp = __uqtoa(uqval, cp, base, 7441573Srgrimes flags & ALT, xdigs); 7451573Srgrimes } else { 7461573Srgrimes if (ulval != 0 || prec != 0) 7471573Srgrimes cp = __ultoa(ulval, cp, base, 7481573Srgrimes flags & ALT, xdigs); 7491573Srgrimes } 7501573Srgrimes size = buf + BUF - cp; 7511573Srgrimes break; 7521573Srgrimes default: /* "%?" prints ?, unless ? is NUL */ 7531573Srgrimes if (ch == '\0') 7541573Srgrimes goto done; 7551573Srgrimes /* pretend it was %c with argument ch */ 7561573Srgrimes cp = buf; 7571573Srgrimes *cp = ch; 7581573Srgrimes size = 1; 7591573Srgrimes sign = '\0'; 7601573Srgrimes break; 7611573Srgrimes } 7621573Srgrimes 7631573Srgrimes /* 7641573Srgrimes * All reasonable formats wind up here. At this point, `cp' 7651573Srgrimes * points to a string which (if not flags&LADJUST) should be 7661573Srgrimes * padded out to `width' places. If flags&ZEROPAD, it should 7671573Srgrimes * first be prefixed by any sign or other prefix; otherwise, 7681573Srgrimes * it should be blank padded before the prefix is emitted. 7691573Srgrimes * After any left-hand padding and prefixing, emit zeroes 7701573Srgrimes * required by a decimal [diouxX] precision, then print the 7711573Srgrimes * string proper, then emit zeroes required by any leftover 7721573Srgrimes * floating precision; finally, if LADJUST, pad with blanks. 7731573Srgrimes * 7741573Srgrimes * Compute actual size, so we know how much to pad. 77514727Sfenner * size excludes decimal prec; realsz includes it. 7761573Srgrimes */ 77714727Sfenner realsz = dprec > size ? dprec : size; 7781573Srgrimes if (sign) 77914727Sfenner realsz++; 7801573Srgrimes else if (flags & HEXPREFIX) 78114727Sfenner realsz += 2; 7821573Srgrimes 7831573Srgrimes /* right-adjusting blank padding */ 7841573Srgrimes if ((flags & (LADJUST|ZEROPAD)) == 0) 7851573Srgrimes PAD(width - realsz, blanks); 7861573Srgrimes 7871573Srgrimes /* prefix */ 7881573Srgrimes if (sign) { 7891573Srgrimes PRINT(&sign, 1); 7901573Srgrimes } else if (flags & HEXPREFIX) { 7911573Srgrimes ox[0] = '0'; 7921573Srgrimes ox[1] = ch; 7931573Srgrimes PRINT(ox, 2); 7941573Srgrimes } 7951573Srgrimes 7961573Srgrimes /* right-adjusting zero padding */ 7971573Srgrimes if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 7981573Srgrimes PAD(width - realsz, zeroes); 7991573Srgrimes 8001573Srgrimes /* leading zeroes from decimal precision */ 80114727Sfenner PAD(dprec - size, zeroes); 8021573Srgrimes 8031573Srgrimes /* the string or number proper */ 8041573Srgrimes#ifdef FLOATING_POINT 8051573Srgrimes if ((flags & FPT) == 0) { 8061573Srgrimes PRINT(cp, size); 8071573Srgrimes } else { /* glue together f_p fragments */ 8081573Srgrimes if (ch >= 'f') { /* 'f' or 'g' */ 8091573Srgrimes if (_double == 0) { 8107033Sbde /* kludge for __dtoa irregularity */ 8117649Sbde if (expt >= ndig && 8127649Sbde (flags & ALT) == 0) { 8131573Srgrimes PRINT("0", 1); 8141573Srgrimes } else { 8151573Srgrimes PRINT("0.", 2); 8161573Srgrimes PAD(ndig - 1, zeroes); 8171573Srgrimes } 8181573Srgrimes } else if (expt <= 0) { 8191573Srgrimes PRINT("0.", 2); 8201573Srgrimes PAD(-expt, zeroes); 8211573Srgrimes PRINT(cp, ndig); 8221573Srgrimes } else if (expt >= ndig) { 8231573Srgrimes PRINT(cp, ndig); 8241573Srgrimes PAD(expt - ndig, zeroes); 8251573Srgrimes if (flags & ALT) 8261573Srgrimes PRINT(".", 1); 8271573Srgrimes } else { 8281573Srgrimes PRINT(cp, expt); 8291573Srgrimes cp += expt; 8301573Srgrimes PRINT(".", 1); 8311573Srgrimes PRINT(cp, ndig-expt); 8321573Srgrimes } 8331573Srgrimes } else { /* 'e' or 'E' */ 8341573Srgrimes if (ndig > 1 || flags & ALT) { 8351573Srgrimes ox[0] = *cp++; 8361573Srgrimes ox[1] = '.'; 8371573Srgrimes PRINT(ox, 2); 8387036Sbde if (_double) { 8391573Srgrimes PRINT(cp, ndig-1); 8401573Srgrimes } else /* 0.[0..] */ 8411573Srgrimes /* __dtoa irregularity */ 8421573Srgrimes PAD(ndig - 1, zeroes); 8431573Srgrimes } else /* XeYYY */ 8441573Srgrimes PRINT(cp, 1); 8451573Srgrimes PRINT(expstr, expsize); 8461573Srgrimes } 8471573Srgrimes } 8481573Srgrimes#else 8491573Srgrimes PRINT(cp, size); 8501573Srgrimes#endif 8511573Srgrimes /* left-adjusting padding (always blank) */ 8521573Srgrimes if (flags & LADJUST) 8531573Srgrimes PAD(width - realsz, blanks); 8541573Srgrimes 8551573Srgrimes /* finally, adjust ret */ 8561573Srgrimes ret += width > realsz ? width : realsz; 8571573Srgrimes 8581573Srgrimes FLUSH(); /* copy out the I/O vectors */ 8591573Srgrimes } 8601573Srgrimesdone: 8611573Srgrimes FLUSH(); 8621573Srgrimeserror: 86313545Sjulian if (__sferror(fp)) 86413545Sjulian ret = EOF; 86513545Sjulian#ifdef _THREAD_SAFE 86613545Sjulian _thread_funlockfile(fp); 86713545Sjulian#endif 86821674Sjkh if ((argtable != NULL) && (argtable != statargtable)) 86921674Sjkh free (argtable); 87013545Sjulian return (ret); 8711573Srgrimes /* NOTREACHED */ 8721573Srgrimes} 8731573Srgrimes 87421674Sjkh/* 87521674Sjkh * Type ids for argument type table. 87621674Sjkh */ 87721674Sjkh#define T_UNUSED 0 87821674Sjkh#define T_SHORT 1 87921674Sjkh#define T_U_SHORT 2 88021674Sjkh#define TP_SHORT 3 88121674Sjkh#define T_INT 4 88221674Sjkh#define T_U_INT 5 88321674Sjkh#define TP_INT 6 88421674Sjkh#define T_LONG 7 88521674Sjkh#define T_U_LONG 8 88621674Sjkh#define TP_LONG 9 88721674Sjkh#define T_QUAD 10 88821674Sjkh#define T_U_QUAD 11 88921674Sjkh#define TP_QUAD 12 89021674Sjkh#define T_DOUBLE 13 89121674Sjkh#define T_LONG_DOUBLE 14 89221674Sjkh#define TP_CHAR 15 89321674Sjkh#define TP_VOID 16 89421674Sjkh 89521674Sjkh/* 89621674Sjkh * Find all arguments when a positional parameter is encountered. Returns a 89721674Sjkh * table, indexed by argument number, of pointers to each arguments. The 89821674Sjkh * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. 89921674Sjkh * It will be replaces with a malloc-ed on if it overflows. 90021674Sjkh */ 90121674Sjkhstatic void 90221674Sjkh__find_arguments (fmt0, ap, argtable) 90321674Sjkh const char *fmt0; 90421674Sjkh va_list ap; 90521674Sjkh void ***argtable; 90621674Sjkh{ 90721674Sjkh register char *fmt; /* format string */ 90821674Sjkh register int ch; /* character from fmt */ 90921674Sjkh register int n, n2; /* handy integer (short term usage) */ 91021674Sjkh register char *cp; /* handy char pointer (short term usage) */ 91121674Sjkh register int flags; /* flags as above */ 91221674Sjkh int width; /* width from format (%8d), or 0 */ 91321674Sjkh unsigned char *typetable; /* table of types */ 91421674Sjkh unsigned char stattypetable [STATIC_ARG_TBL_SIZE]; 91521674Sjkh int tablesize; /* current size of type table */ 91621674Sjkh int tablemax; /* largest used index in table */ 91721674Sjkh int nextarg; /* 1-based argument index */ 91821674Sjkh 91921674Sjkh /* 92021674Sjkh * Add an argument type to the table, expanding if necessary. 92121674Sjkh */ 92221674Sjkh#define ADDTYPE(type) \ 92321674Sjkh ((nextarg >= tablesize) ? \ 92421674Sjkh __grow_type_table(nextarg, &typetable, &tablesize) : 0, \ 92521674Sjkh typetable[nextarg++] = type, \ 92621674Sjkh (nextarg > tablemax) ? tablemax = nextarg : 0) 92721674Sjkh 92821674Sjkh#define ADDSARG() \ 92921674Sjkh ((flags&LONGINT) ? ADDTYPE(T_LONG) : \ 93021674Sjkh ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT))) 93121674Sjkh 93221674Sjkh#define ADDUARG() \ 93321674Sjkh ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \ 93421674Sjkh ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT))) 93521674Sjkh 93621674Sjkh /* 93721674Sjkh * Add * arguments to the type array. 93821674Sjkh */ 93921674Sjkh#define ADDASTER() \ 94021674Sjkh n2 = 0; \ 94121674Sjkh cp = fmt; \ 94221674Sjkh while (is_digit(*cp)) { \ 94321674Sjkh n2 = 10 * n2 + to_digit(*cp); \ 94421674Sjkh cp++; \ 94521674Sjkh } \ 94621674Sjkh if (*cp == '$') { \ 94721674Sjkh int hold = nextarg; \ 94821674Sjkh nextarg = n2; \ 94921674Sjkh ADDTYPE (T_INT); \ 95021674Sjkh nextarg = hold; \ 95121674Sjkh fmt = ++cp; \ 95221674Sjkh } else { \ 95321674Sjkh ADDTYPE (T_INT); \ 95421674Sjkh } 95521674Sjkh fmt = (char *)fmt0; 95621674Sjkh typetable = stattypetable; 95721674Sjkh tablesize = STATIC_ARG_TBL_SIZE; 95821674Sjkh tablemax = 0; 95921674Sjkh nextarg = 1; 96021674Sjkh memset (typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); 96121674Sjkh 96221674Sjkh /* 96321674Sjkh * Scan the format for conversions (`%' character). 96421674Sjkh */ 96521674Sjkh for (;;) { 96621674Sjkh for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 96721674Sjkh /* void */; 96821674Sjkh if (ch == '\0') 96921674Sjkh goto done; 97021674Sjkh fmt++; /* skip over '%' */ 97121674Sjkh 97221674Sjkh flags = 0; 97321674Sjkh width = 0; 97421674Sjkh 97521674Sjkhrflag: ch = *fmt++; 97621674Sjkhreswitch: switch (ch) { 97721674Sjkh case ' ': 97821674Sjkh case '#': 97921674Sjkh goto rflag; 98021674Sjkh case '*': 98121674Sjkh ADDASTER (); 98221674Sjkh goto rflag; 98321674Sjkh case '-': 98421674Sjkh case '+': 98521674Sjkh goto rflag; 98621674Sjkh case '.': 98721674Sjkh if ((ch = *fmt++) == '*') { 98821674Sjkh ADDASTER (); 98921674Sjkh goto rflag; 99021674Sjkh } 99121674Sjkh while (is_digit(ch)) { 99221674Sjkh ch = *fmt++; 99321674Sjkh } 99421674Sjkh goto reswitch; 99521674Sjkh case '0': 99621674Sjkh goto rflag; 99721674Sjkh case '1': case '2': case '3': case '4': 99821674Sjkh case '5': case '6': case '7': case '8': case '9': 99921674Sjkh n = 0; 100021674Sjkh do { 100121674Sjkh n = 10 * n + to_digit(ch); 100221674Sjkh ch = *fmt++; 100321674Sjkh } while (is_digit(ch)); 100421674Sjkh if (ch == '$') { 100521674Sjkh nextarg = n; 100621674Sjkh goto rflag; 100721674Sjkh } 100821674Sjkh width = n; 100921674Sjkh goto reswitch; 10101573Srgrimes#ifdef FLOATING_POINT 101121674Sjkh case 'L': 101221674Sjkh flags |= LONGDBL; 101321674Sjkh goto rflag; 101421674Sjkh#endif 101521674Sjkh case 'h': 101621674Sjkh flags |= SHORTINT; 101721674Sjkh goto rflag; 101821674Sjkh case 'l': 101921674Sjkh flags |= LONGINT; 102021674Sjkh goto rflag; 102121674Sjkh case 'q': 102221674Sjkh flags |= QUADINT; 102321674Sjkh goto rflag; 102421674Sjkh case 'c': 102521674Sjkh ADDTYPE(T_INT); 102621674Sjkh break; 102721674Sjkh case 'D': 102821674Sjkh flags |= LONGINT; 102921674Sjkh /*FALLTHROUGH*/ 103021674Sjkh case 'd': 103121674Sjkh case 'i': 103221674Sjkh if (flags & QUADINT) { 103321674Sjkh ADDTYPE(T_QUAD); 103421674Sjkh } else { 103521674Sjkh ADDSARG(); 103621674Sjkh } 103721674Sjkh break; 103821674Sjkh#ifdef FLOATING_POINT 103921674Sjkh case 'e': 104021674Sjkh case 'E': 104121674Sjkh case 'f': 104221674Sjkh case 'g': 104321674Sjkh case 'G': 104421674Sjkh if (flags & LONGDBL) 104521674Sjkh ADDTYPE(T_LONG_DOUBLE); 104621674Sjkh else 104721674Sjkh ADDTYPE(T_DOUBLE); 104821674Sjkh break; 104921674Sjkh#endif /* FLOATING_POINT */ 105021674Sjkh case 'n': 105121674Sjkh if (flags & QUADINT) 105221674Sjkh ADDTYPE(TP_QUAD); 105321674Sjkh else if (flags & LONGINT) 105421674Sjkh ADDTYPE(TP_LONG); 105521674Sjkh else if (flags & SHORTINT) 105621674Sjkh ADDTYPE(TP_SHORT); 105721674Sjkh else 105821674Sjkh ADDTYPE(TP_INT); 105921674Sjkh continue; /* no output */ 106021674Sjkh case 'O': 106121674Sjkh flags |= LONGINT; 106221674Sjkh /*FALLTHROUGH*/ 106321674Sjkh case 'o': 106421674Sjkh if (flags & QUADINT) 106521674Sjkh ADDTYPE(T_U_QUAD); 106621674Sjkh else 106721674Sjkh ADDUARG(); 106821674Sjkh break; 106921674Sjkh case 'p': 107021674Sjkh ADDTYPE(TP_VOID); 107121674Sjkh break; 107221674Sjkh case 's': 107321674Sjkh ADDTYPE(TP_CHAR); 107421674Sjkh break; 107521674Sjkh case 'U': 107621674Sjkh flags |= LONGINT; 107721674Sjkh /*FALLTHROUGH*/ 107821674Sjkh case 'u': 107921674Sjkh if (flags & QUADINT) 108021674Sjkh ADDTYPE(T_U_QUAD); 108121674Sjkh else 108221674Sjkh ADDUARG(); 108321674Sjkh break; 108421674Sjkh case 'X': 108521674Sjkh case 'x': 108621674Sjkh if (flags & QUADINT) 108721674Sjkh ADDTYPE(T_U_QUAD); 108821674Sjkh else 108921674Sjkh ADDUARG(); 109021674Sjkh break; 109121674Sjkh default: /* "%?" prints ?, unless ? is NUL */ 109221674Sjkh if (ch == '\0') 109321674Sjkh goto done; 109421674Sjkh break; 109521674Sjkh } 109621674Sjkh } 109721674Sjkhdone: 109821674Sjkh /* 109921674Sjkh * Build the argument table. 110021674Sjkh */ 110121674Sjkh if (tablemax >= STATIC_ARG_TBL_SIZE) { 110221674Sjkh *argtable = (void **) 110321674Sjkh malloc (sizeof (void *) * (tablemax + 1)); 110421674Sjkh } 11051573Srgrimes 110621674Sjkh (*argtable) [0] = NULL; 110721674Sjkh for (n = 1; n <= tablemax; n++) { 110821674Sjkh (*argtable) [n] = ap; 110921674Sjkh switch (typetable [n]) { 111021674Sjkh case T_UNUSED: 111121674Sjkh (void) va_arg (ap, int); 111221674Sjkh break; 111321674Sjkh case T_SHORT: 111421674Sjkh (void) va_arg (ap, int); 111521674Sjkh break; 111621674Sjkh case T_U_SHORT: 111721674Sjkh (void) va_arg (ap, int); 111821674Sjkh break; 111921674Sjkh case TP_SHORT: 112021674Sjkh (void) va_arg (ap, short *); 112121674Sjkh break; 112221674Sjkh case T_INT: 112321674Sjkh (void) va_arg (ap, int); 112421674Sjkh break; 112521674Sjkh case T_U_INT: 112621674Sjkh (void) va_arg (ap, unsigned int); 112721674Sjkh break; 112821674Sjkh case TP_INT: 112921674Sjkh (void) va_arg (ap, int *); 113021674Sjkh break; 113121674Sjkh case T_LONG: 113221674Sjkh (void) va_arg (ap, long); 113321674Sjkh break; 113421674Sjkh case T_U_LONG: 113521674Sjkh (void) va_arg (ap, unsigned long); 113621674Sjkh break; 113721674Sjkh case TP_LONG: 113821674Sjkh (void) va_arg (ap, long *); 113921674Sjkh break; 114021674Sjkh case T_QUAD: 114121674Sjkh (void) va_arg (ap, quad_t); 114221674Sjkh break; 114321674Sjkh case T_U_QUAD: 114421674Sjkh (void) va_arg (ap, u_quad_t); 114521674Sjkh break; 114621674Sjkh case TP_QUAD: 114721674Sjkh (void) va_arg (ap, quad_t *); 114821674Sjkh break; 114921674Sjkh case T_DOUBLE: 115021674Sjkh (void) va_arg (ap, double); 115121674Sjkh break; 115221674Sjkh case T_LONG_DOUBLE: 115321674Sjkh (void) va_arg (ap, long double); 115421674Sjkh break; 115521674Sjkh case TP_CHAR: 115621674Sjkh (void) va_arg (ap, char *); 115721674Sjkh break; 115821674Sjkh case TP_VOID: 115921674Sjkh (void) va_arg (ap, void *); 116021674Sjkh break; 116121674Sjkh } 116221674Sjkh } 116321674Sjkh 116421674Sjkh if ((typetable != NULL) && (typetable != stattypetable)) 116521674Sjkh free (typetable); 116621674Sjkh} 116721674Sjkh 116821674Sjkh/* 116921674Sjkh * Increase the size of the type table. 117021674Sjkh */ 117121674Sjkhstatic void 117221674Sjkh__grow_type_table (nextarg, typetable, tablesize) 117321674Sjkh int nextarg; 117421674Sjkh unsigned char **typetable; 117521674Sjkh int *tablesize; 117621674Sjkh{ 117721674Sjkh unsigned char *oldtable = *typetable; 117821674Sjkh int newsize = *tablesize * 2; 117921674Sjkh 118021674Sjkh if (*tablesize == STATIC_ARG_TBL_SIZE) { 118121674Sjkh *typetable = (unsigned char *) 118221674Sjkh malloc (sizeof (unsigned char) * newsize); 118321674Sjkh bcopy (oldtable, *typetable, *tablesize); 118421674Sjkh } else { 118521674Sjkh *typetable = (unsigned char *) 118621674Sjkh realloc (typetable, sizeof (unsigned char) * newsize); 118721674Sjkh 118821674Sjkh } 118921674Sjkh memset (&typetable [*tablesize], T_UNUSED, (newsize - *tablesize)); 119021674Sjkh 119121674Sjkh *tablesize = newsize; 119221674Sjkh} 119321674Sjkh 119421674Sjkh 119521674Sjkh#ifdef FLOATING_POINT 119621674Sjkh 11971573Srgrimesextern char *__dtoa __P((double, int, int, int *, int *, char **)); 11981573Srgrimes 11991573Srgrimesstatic char * 12001573Srgrimescvt(value, ndigits, flags, sign, decpt, ch, length) 12011573Srgrimes double value; 12021573Srgrimes int ndigits, flags, *decpt, ch, *length; 12031573Srgrimes char *sign; 12041573Srgrimes{ 12051573Srgrimes int mode, dsgn; 12061573Srgrimes char *digits, *bp, *rve; 12071573Srgrimes 12081573Srgrimes if (ch == 'f') 12097033Sbde mode = 3; /* ndigits after the decimal point */ 12101573Srgrimes else { 12117033Sbde /* 12128870Srgrimes * To obtain ndigits after the decimal point for the 'e' 12138870Srgrimes * and 'E' formats, round to ndigits + 1 significant 12147033Sbde * figures. 12157033Sbde */ 12167033Sbde if (ch == 'e' || ch == 'E') 12177033Sbde ndigits++; 12187033Sbde mode = 2; /* ndigits significant digits */ 12191573Srgrimes } 12201573Srgrimes if (value < 0) { 12211573Srgrimes value = -value; 12221573Srgrimes *sign = '-'; 12231573Srgrimes } else 12241573Srgrimes *sign = '\000'; 12251573Srgrimes digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve); 12267033Sbde if ((ch != 'g' && ch != 'G') || flags & ALT) { 12277033Sbde /* print trailing zeros */ 12281573Srgrimes bp = digits + ndigits; 12291573Srgrimes if (ch == 'f') { 12301573Srgrimes if (*digits == '0' && value) 12311573Srgrimes *decpt = -ndigits + 1; 12321573Srgrimes bp += *decpt; 12331573Srgrimes } 12341573Srgrimes if (value == 0) /* kludge for __dtoa irregularity */ 12351573Srgrimes rve = bp; 12361573Srgrimes while (rve < bp) 12371573Srgrimes *rve++ = '0'; 12381573Srgrimes } 12391573Srgrimes *length = rve - digits; 12401573Srgrimes return (digits); 12411573Srgrimes} 12421573Srgrimes 12431573Srgrimesstatic int 12441573Srgrimesexponent(p0, exp, fmtch) 12451573Srgrimes char *p0; 12461573Srgrimes int exp, fmtch; 12471573Srgrimes{ 12481573Srgrimes register char *p, *t; 12491573Srgrimes char expbuf[MAXEXP]; 12501573Srgrimes 12511573Srgrimes p = p0; 12521573Srgrimes *p++ = fmtch; 12531573Srgrimes if (exp < 0) { 12541573Srgrimes exp = -exp; 12551573Srgrimes *p++ = '-'; 12561573Srgrimes } 12571573Srgrimes else 12581573Srgrimes *p++ = '+'; 12591573Srgrimes t = expbuf + MAXEXP; 12601573Srgrimes if (exp > 9) { 12611573Srgrimes do { 12621573Srgrimes *--t = to_char(exp % 10); 12631573Srgrimes } while ((exp /= 10) > 9); 12641573Srgrimes *--t = to_char(exp); 12651573Srgrimes for (; t < expbuf + MAXEXP; *p++ = *t++); 12661573Srgrimes } 12671573Srgrimes else { 12681573Srgrimes *p++ = '0'; 12691573Srgrimes *p++ = to_char(exp); 12701573Srgrimes } 12711573Srgrimes return (p - p0); 12721573Srgrimes} 12731573Srgrimes#endif /* FLOATING_POINT */ 1274