| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 1 | /*	$OpenBSD: vfprintf.c,v 1.37 2006/01/13 17:56:18 millert Exp $	*/ | 
|  | 2 | /*- | 
|  | 3 | * Copyright (c) 1990 The Regents of the University of California. | 
|  | 4 | * All rights reserved. | 
|  | 5 | * | 
|  | 6 | * This code is derived from software contributed to Berkeley by | 
|  | 7 | * Chris Torek. | 
|  | 8 | * | 
|  | 9 | * Redistribution and use in source and binary forms, with or without | 
|  | 10 | * modification, are permitted provided that the following conditions | 
|  | 11 | * are met: | 
|  | 12 | * 1. Redistributions of source code must retain the above copyright | 
|  | 13 | *    notice, this list of conditions and the following disclaimer. | 
|  | 14 | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | 15 | *    notice, this list of conditions and the following disclaimer in the | 
|  | 16 | *    documentation and/or other materials provided with the distribution. | 
|  | 17 | * 3. Neither the name of the University nor the names of its contributors | 
|  | 18 | *    may be used to endorse or promote products derived from this software | 
|  | 19 | *    without specific prior written permission. | 
|  | 20 | * | 
|  | 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
|  | 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | 24 | * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
|  | 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | 31 | * SUCH DAMAGE. | 
|  | 32 | */ | 
|  | 33 |  | 
|  | 34 | /* | 
|  | 35 | * Actual printf innards. | 
|  | 36 | * | 
|  | 37 | * This code is large and complicated... | 
|  | 38 | */ | 
|  | 39 |  | 
|  | 40 | #include <sys/types.h> | 
|  | 41 | #include <sys/mman.h> | 
|  | 42 |  | 
|  | 43 | #include <errno.h> | 
|  | 44 | #include <stdarg.h> | 
|  | 45 | #include <stddef.h> | 
|  | 46 | #include <stdio.h> | 
|  | 47 | #include <stdint.h> | 
|  | 48 | #include <stdlib.h> | 
|  | 49 | #include <string.h> | 
|  | 50 |  | 
|  | 51 | #include "local.h" | 
|  | 52 | #include "fvwrite.h" | 
|  | 53 |  | 
|  | 54 | static void __find_arguments(const char *fmt0, va_list ap, va_list **argtable, | 
|  | 55 | size_t *argtablesiz); | 
|  | 56 | static int __grow_type_table(unsigned char **typetable, int *tablesize); | 
|  | 57 |  | 
|  | 58 | /* | 
|  | 59 | * Flush out all the vectors defined by the given uio, | 
|  | 60 | * then reset it so that it can be reused. | 
|  | 61 | */ | 
|  | 62 | static int | 
|  | 63 | __sprint(FILE *fp, struct __suio *uio) | 
|  | 64 | { | 
|  | 65 | int err; | 
|  | 66 |  | 
|  | 67 | if (uio->uio_resid == 0) { | 
|  | 68 | uio->uio_iovcnt = 0; | 
|  | 69 | return (0); | 
|  | 70 | } | 
|  | 71 | err = __sfvwrite(fp, uio); | 
|  | 72 | uio->uio_resid = 0; | 
|  | 73 | uio->uio_iovcnt = 0; | 
|  | 74 | return (err); | 
|  | 75 | } | 
|  | 76 |  | 
|  | 77 | /* | 
|  | 78 | * Helper function for `fprintf to unbuffered unix file': creates a | 
|  | 79 | * temporary buffer.  We only work on write-only files; this avoids | 
|  | 80 | * worries about ungetc buffers and so forth. | 
|  | 81 | */ | 
|  | 82 | static int | 
|  | 83 | __sbprintf(FILE *fp, const char *fmt, va_list ap) | 
|  | 84 | { | 
|  | 85 | int ret; | 
|  | 86 | FILE fake; | 
|  | 87 | struct __sfileext fakeext; | 
|  | 88 | unsigned char buf[BUFSIZ]; | 
|  | 89 |  | 
|  | 90 | _FILEEXT_SETUP(&fake, &fakeext); | 
|  | 91 | /* copy the important variables */ | 
|  | 92 | fake._flags = fp->_flags & ~__SNBF; | 
|  | 93 | fake._file = fp->_file; | 
|  | 94 | fake._cookie = fp->_cookie; | 
|  | 95 | fake._write = fp->_write; | 
|  | 96 |  | 
|  | 97 | /* set up the buffer */ | 
|  | 98 | fake._bf._base = fake._p = buf; | 
|  | 99 | fake._bf._size = fake._w = sizeof(buf); | 
|  | 100 | fake._lbfsize = 0;	/* not actually used, but Just In Case */ | 
|  | 101 |  | 
|  | 102 | /* do the work, then copy any error status */ | 
| Kenny Root | f582340 | 2011-02-12 07:13:44 -0800 | [diff] [blame] | 103 | ret = __vfprintf(&fake, fmt, ap); | 
|  | 104 | if (ret >= 0 && __sflush(&fake)) | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 105 | ret = EOF; | 
|  | 106 | if (fake._flags & __SERR) | 
|  | 107 | fp->_flags |= __SERR; | 
|  | 108 | return (ret); | 
|  | 109 | } | 
|  | 110 |  | 
|  | 111 |  | 
|  | 112 | #ifdef FLOATING_POINT | 
|  | 113 | #include <locale.h> | 
|  | 114 | #include <math.h> | 
|  | 115 | #include "floatio.h" | 
|  | 116 |  | 
|  | 117 | #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */ | 
|  | 118 | #define	DEFPREC		6 | 
|  | 119 |  | 
|  | 120 | static char *cvt(double, int, int, char *, int *, int, int *); | 
|  | 121 | static int exponent(char *, int, int); | 
|  | 122 | #else /* no FLOATING_POINT */ | 
|  | 123 | #define	BUF		40 | 
|  | 124 | #endif /* FLOATING_POINT */ | 
|  | 125 |  | 
|  | 126 | #define STATIC_ARG_TBL_SIZE 8	/* Size of static argument table. */ | 
|  | 127 |  | 
|  | 128 | /* BIONIC: do not link libm for only two rather simple functions */ | 
|  | 129 | #ifdef FLOATING_POINT | 
|  | 130 | static  int  _my_isinf(double); | 
|  | 131 | static  int  _my_isnan(double); | 
|  | 132 | #endif | 
|  | 133 |  | 
|  | 134 | /* | 
|  | 135 | * Macros for converting digits to letters and vice versa | 
|  | 136 | */ | 
|  | 137 | #define	to_digit(c)	((c) - '0') | 
|  | 138 | #define is_digit(c)	((unsigned)to_digit(c) <= 9) | 
|  | 139 | #define	to_char(n)	((n) + '0') | 
|  | 140 |  | 
|  | 141 | /* | 
|  | 142 | * Flags used during conversion. | 
|  | 143 | */ | 
|  | 144 | #define	ALT		0x0001		/* alternate form */ | 
|  | 145 | #define	HEXPREFIX	0x0002		/* add 0x or 0X prefix */ | 
|  | 146 | #define	LADJUST		0x0004		/* left adjustment */ | 
|  | 147 | #define	LONGDBL		0x0008		/* long double; unimplemented */ | 
|  | 148 | #define	LONGINT		0x0010		/* long integer */ | 
|  | 149 | #define	LLONGINT	0x0020		/* long long integer */ | 
|  | 150 | #define	SHORTINT	0x0040		/* short integer */ | 
|  | 151 | #define	ZEROPAD		0x0080		/* zero (as opposed to blank) pad */ | 
|  | 152 | #define FPT		0x0100		/* Floating point number */ | 
|  | 153 | #define PTRINT		0x0200		/* (unsigned) ptrdiff_t */ | 
|  | 154 | #define SIZEINT		0x0400		/* (signed) size_t */ | 
|  | 155 | #define CHARINT		0x0800		/* 8 bit integer */ | 
|  | 156 | #define MAXINT		0x1000		/* largest integer size (intmax_t) */ | 
|  | 157 |  | 
|  | 158 | int | 
|  | 159 | vfprintf(FILE *fp, const char *fmt0, __va_list ap) | 
|  | 160 | { | 
| Kenny Root | f582340 | 2011-02-12 07:13:44 -0800 | [diff] [blame] | 161 | int ret; | 
|  | 162 |  | 
|  | 163 | FLOCKFILE(fp); | 
|  | 164 | ret = __vfprintf(fp, fmt0, ap); | 
|  | 165 | FUNLOCKFILE(fp); | 
|  | 166 | return (ret); | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 | int | 
|  | 170 | __vfprintf(FILE *fp, const char *fmt0, __va_list ap) | 
|  | 171 | { | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 172 | char *fmt;	/* format string */ | 
|  | 173 | int ch;	/* character from fmt */ | 
|  | 174 | int n, m, n2;	/* handy integers (short term usage) */ | 
|  | 175 | char *cp;	/* handy char pointer (short term usage) */ | 
|  | 176 | char *cp_free = NULL;  /* BIONIC: copy of cp to be freed after usage */ | 
|  | 177 | struct __siov *iovp;/* for PRINT macro */ | 
|  | 178 | int flags;	/* flags as above */ | 
|  | 179 | int ret;		/* return value accumulator */ | 
|  | 180 | int width;		/* width from format (%8d), or 0 */ | 
|  | 181 | int prec;		/* precision from format (%.3d), or -1 */ | 
|  | 182 | char sign;		/* sign prefix (' ', '+', '-', or \0) */ | 
|  | 183 | wchar_t wc; | 
|  | 184 | void* ps; | 
|  | 185 | #ifdef FLOATING_POINT | 
|  | 186 | char *decimal_point = "."; | 
|  | 187 | char softsign;		/* temporary negative sign for floats */ | 
|  | 188 | double _double = 0.;	/* double precision arguments %[eEfgG] */ | 
|  | 189 | int expt;		/* integer value of exponent */ | 
|  | 190 | int expsize = 0;	/* character count for expstr */ | 
|  | 191 | int ndig;		/* actual number of digits returned by cvt */ | 
|  | 192 | char expstr[7];		/* buffer for exponent string */ | 
|  | 193 | #endif | 
|  | 194 |  | 
|  | 195 | uintmax_t _umax;	/* integer arguments %[diouxX] */ | 
|  | 196 | enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ | 
|  | 197 | int dprec;		/* a copy of prec if [diouxX], 0 otherwise */ | 
|  | 198 | int realsz;		/* field size expanded by dprec */ | 
|  | 199 | int size;		/* size of converted field or string */ | 
|  | 200 | char* xdigs = NULL;		/* digits for [xX] conversion */ | 
|  | 201 | #define NIOV 8 | 
|  | 202 | struct __suio uio;	/* output information: summary */ | 
|  | 203 | struct __siov iov[NIOV];/* ... and individual io vectors */ | 
|  | 204 | char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */ | 
|  | 205 | char ox[2];		/* space for 0x hex-prefix */ | 
|  | 206 | va_list *argtable;	/* args, built due to positional arg */ | 
|  | 207 | va_list statargtable[STATIC_ARG_TBL_SIZE]; | 
|  | 208 | size_t argtablesiz; | 
|  | 209 | int nextarg;		/* 1-based argument index */ | 
|  | 210 | va_list orgap;		/* original argument pointer */ | 
|  | 211 | /* | 
|  | 212 | * Choose PADSIZE to trade efficiency vs. size.  If larger printf | 
|  | 213 | * fields occur frequently, increase PADSIZE and make the initialisers | 
|  | 214 | * below longer. | 
|  | 215 | */ | 
|  | 216 | #define	PADSIZE	16		/* pad chunk size */ | 
| Glenn Kasten | 0946b1f | 2011-01-09 11:28:22 -0800 | [diff] [blame] | 217 | static const char blanks[PADSIZE] = | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 218 | {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; | 
| Glenn Kasten | 0946b1f | 2011-01-09 11:28:22 -0800 | [diff] [blame] | 219 | static const char zeroes[PADSIZE] = | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 220 | {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; | 
|  | 221 |  | 
|  | 222 | /* | 
|  | 223 | * BEWARE, these `goto error' on error, and PAD uses `n'. | 
|  | 224 | */ | 
|  | 225 | #define	PRINT(ptr, len) do { \ | 
|  | 226 | iovp->iov_base = (ptr); \ | 
|  | 227 | iovp->iov_len = (len); \ | 
|  | 228 | uio.uio_resid += (len); \ | 
|  | 229 | iovp++; \ | 
|  | 230 | if (++uio.uio_iovcnt >= NIOV) { \ | 
|  | 231 | if (__sprint(fp, &uio)) \ | 
|  | 232 | goto error; \ | 
|  | 233 | iovp = iov; \ | 
|  | 234 | } \ | 
|  | 235 | } while (0) | 
|  | 236 | #define	PAD(howmany, with) do { \ | 
|  | 237 | if ((n = (howmany)) > 0) { \ | 
|  | 238 | while (n > PADSIZE) { \ | 
|  | 239 | PRINT(with, PADSIZE); \ | 
|  | 240 | n -= PADSIZE; \ | 
|  | 241 | } \ | 
|  | 242 | PRINT(with, n); \ | 
|  | 243 | } \ | 
|  | 244 | } while (0) | 
|  | 245 | #define	FLUSH() do { \ | 
|  | 246 | if (uio.uio_resid && __sprint(fp, &uio)) \ | 
|  | 247 | goto error; \ | 
|  | 248 | uio.uio_iovcnt = 0; \ | 
|  | 249 | iovp = iov; \ | 
|  | 250 | } while (0) | 
|  | 251 |  | 
|  | 252 | /* | 
|  | 253 | * To extend shorts properly, we need both signed and unsigned | 
|  | 254 | * argument extraction methods. | 
|  | 255 | */ | 
|  | 256 | #define	SARG() \ | 
|  | 257 | ((intmax_t)(flags&MAXINT ? GETARG(intmax_t) : \ | 
|  | 258 | flags&LLONGINT ? GETARG(long long) : \ | 
|  | 259 | flags&LONGINT ? GETARG(long) : \ | 
|  | 260 | flags&PTRINT ? GETARG(ptrdiff_t) : \ | 
|  | 261 | flags&SIZEINT ? GETARG(ssize_t) : \ | 
|  | 262 | flags&SHORTINT ? (short)GETARG(int) : \ | 
|  | 263 | flags&CHARINT ? (__signed char)GETARG(int) : \ | 
|  | 264 | GETARG(int))) | 
|  | 265 | #define	UARG() \ | 
|  | 266 | ((uintmax_t)(flags&MAXINT ? GETARG(uintmax_t) : \ | 
|  | 267 | flags&LLONGINT ? GETARG(unsigned long long) : \ | 
|  | 268 | flags&LONGINT ? GETARG(unsigned long) : \ | 
|  | 269 | flags&PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \ | 
|  | 270 | flags&SIZEINT ? GETARG(size_t) : \ | 
|  | 271 | flags&SHORTINT ? (unsigned short)GETARG(int) : \ | 
|  | 272 | flags&CHARINT ? (unsigned char)GETARG(int) : \ | 
|  | 273 | GETARG(unsigned int))) | 
|  | 274 |  | 
|  | 275 | /* | 
|  | 276 | * Get * arguments, including the form *nn$.  Preserve the nextarg | 
|  | 277 | * that the argument can be gotten once the type is determined. | 
|  | 278 | */ | 
|  | 279 | #define GETASTER(val) \ | 
|  | 280 | n2 = 0; \ | 
|  | 281 | cp = fmt; \ | 
|  | 282 | while (is_digit(*cp)) { \ | 
|  | 283 | n2 = 10 * n2 + to_digit(*cp); \ | 
|  | 284 | cp++; \ | 
|  | 285 | } \ | 
|  | 286 | if (*cp == '$') { \ | 
|  | 287 | int hold = nextarg; \ | 
|  | 288 | if (argtable == NULL) { \ | 
|  | 289 | argtable = statargtable; \ | 
|  | 290 | __find_arguments(fmt0, orgap, &argtable, &argtablesiz); \ | 
|  | 291 | } \ | 
|  | 292 | nextarg = n2; \ | 
|  | 293 | val = GETARG(int); \ | 
|  | 294 | nextarg = hold; \ | 
|  | 295 | fmt = ++cp; \ | 
|  | 296 | } else { \ | 
|  | 297 | val = GETARG(int); \ | 
|  | 298 | } | 
|  | 299 |  | 
|  | 300 | /* | 
|  | 301 | * Get the argument indexed by nextarg.   If the argument table is | 
|  | 302 | * built, use it to get the argument.  If its not, get the next | 
|  | 303 | * argument (and arguments must be gotten sequentially). | 
|  | 304 | */ | 
|  | 305 | #define GETARG(type) \ | 
|  | 306 | (((argtable != NULL) ? (void)(ap = argtable[nextarg]) : (void)0), \ | 
|  | 307 | nextarg++, va_arg(ap, type)) | 
|  | 308 |  | 
|  | 309 | _SET_ORIENTATION(fp, -1); | 
|  | 310 | /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ | 
|  | 311 | if (cantwrite(fp)) { | 
|  | 312 | errno = EBADF; | 
|  | 313 | return (EOF); | 
|  | 314 | } | 
|  | 315 |  | 
|  | 316 | /* optimise fprintf(stderr) (and other unbuffered Unix files) */ | 
|  | 317 | if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && | 
|  | 318 | fp->_file >= 0) | 
|  | 319 | return (__sbprintf(fp, fmt0, ap)); | 
|  | 320 |  | 
|  | 321 | fmt = (char *)fmt0; | 
|  | 322 | argtable = NULL; | 
|  | 323 | nextarg = 1; | 
|  | 324 | va_copy(orgap, ap); | 
|  | 325 | uio.uio_iov = iovp = iov; | 
|  | 326 | uio.uio_resid = 0; | 
|  | 327 | uio.uio_iovcnt = 0; | 
|  | 328 | ret = 0; | 
|  | 329 |  | 
|  | 330 | memset(&ps, 0, sizeof(ps)); | 
|  | 331 | /* | 
|  | 332 | * Scan the format for conversions (`%' character). | 
|  | 333 | */ | 
|  | 334 | for (;;) { | 
|  | 335 | cp = fmt; | 
|  | 336 | #if 1  /* BIONIC */ | 
|  | 337 | n = -1; | 
|  | 338 | while ( (wc = *fmt) != 0 ) { | 
|  | 339 | if (wc == '%') { | 
|  | 340 | n = 1; | 
|  | 341 | break; | 
|  | 342 | } | 
|  | 343 | fmt++; | 
|  | 344 | } | 
|  | 345 | #else | 
|  | 346 | while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0) { | 
|  | 347 | fmt += n; | 
|  | 348 | if (wc == '%') { | 
|  | 349 | fmt--; | 
|  | 350 | break; | 
|  | 351 | } | 
|  | 352 | } | 
|  | 353 | #endif | 
|  | 354 | if ((m = fmt - cp) != 0) { | 
|  | 355 | PRINT(cp, m); | 
|  | 356 | ret += m; | 
|  | 357 | } | 
|  | 358 | if (n <= 0) | 
|  | 359 | goto done; | 
|  | 360 | fmt++;		/* skip over '%' */ | 
|  | 361 |  | 
|  | 362 | flags = 0; | 
|  | 363 | dprec = 0; | 
|  | 364 | width = 0; | 
|  | 365 | prec = -1; | 
|  | 366 | sign = '\0'; | 
|  | 367 |  | 
|  | 368 | rflag:		ch = *fmt++; | 
|  | 369 | reswitch:	switch (ch) { | 
|  | 370 | case ' ': | 
|  | 371 | /* | 
|  | 372 | * ``If the space and + flags both appear, the space | 
|  | 373 | * flag will be ignored.'' | 
|  | 374 | *	-- ANSI X3J11 | 
|  | 375 | */ | 
|  | 376 | if (!sign) | 
|  | 377 | sign = ' '; | 
|  | 378 | goto rflag; | 
|  | 379 | case '#': | 
|  | 380 | flags |= ALT; | 
|  | 381 | goto rflag; | 
|  | 382 | case '*': | 
|  | 383 | /* | 
|  | 384 | * ``A negative field width argument is taken as a | 
|  | 385 | * - flag followed by a positive field width.'' | 
|  | 386 | *	-- ANSI X3J11 | 
|  | 387 | * They don't exclude field widths read from args. | 
|  | 388 | */ | 
|  | 389 | GETASTER(width); | 
|  | 390 | if (width >= 0) | 
|  | 391 | goto rflag; | 
|  | 392 | width = -width; | 
|  | 393 | /* FALLTHROUGH */ | 
|  | 394 | case '-': | 
|  | 395 | flags |= LADJUST; | 
|  | 396 | goto rflag; | 
|  | 397 | case '+': | 
|  | 398 | sign = '+'; | 
|  | 399 | goto rflag; | 
|  | 400 | case '.': | 
|  | 401 | if ((ch = *fmt++) == '*') { | 
|  | 402 | GETASTER(n); | 
|  | 403 | prec = n < 0 ? -1 : n; | 
|  | 404 | goto rflag; | 
|  | 405 | } | 
|  | 406 | n = 0; | 
|  | 407 | while (is_digit(ch)) { | 
|  | 408 | n = 10 * n + to_digit(ch); | 
|  | 409 | ch = *fmt++; | 
|  | 410 | } | 
|  | 411 | if (ch == '$') { | 
|  | 412 | nextarg = n; | 
|  | 413 | if (argtable == NULL) { | 
|  | 414 | argtable = statargtable; | 
|  | 415 | __find_arguments(fmt0, orgap, | 
|  | 416 | &argtable, &argtablesiz); | 
|  | 417 | } | 
|  | 418 | goto rflag; | 
|  | 419 | } | 
|  | 420 | prec = n < 0 ? -1 : n; | 
|  | 421 | goto reswitch; | 
|  | 422 | case '0': | 
|  | 423 | /* | 
|  | 424 | * ``Note that 0 is taken as a flag, not as the | 
|  | 425 | * beginning of a field width.'' | 
|  | 426 | *	-- ANSI X3J11 | 
|  | 427 | */ | 
|  | 428 | flags |= ZEROPAD; | 
|  | 429 | goto rflag; | 
|  | 430 | case '1': case '2': case '3': case '4': | 
|  | 431 | case '5': case '6': case '7': case '8': case '9': | 
|  | 432 | n = 0; | 
|  | 433 | do { | 
|  | 434 | n = 10 * n + to_digit(ch); | 
|  | 435 | ch = *fmt++; | 
|  | 436 | } while (is_digit(ch)); | 
|  | 437 | if (ch == '$') { | 
|  | 438 | nextarg = n; | 
|  | 439 | if (argtable == NULL) { | 
|  | 440 | argtable = statargtable; | 
|  | 441 | __find_arguments(fmt0, orgap, | 
|  | 442 | &argtable, &argtablesiz); | 
|  | 443 | } | 
|  | 444 | goto rflag; | 
|  | 445 | } | 
|  | 446 | width = n; | 
|  | 447 | goto reswitch; | 
|  | 448 | #ifdef FLOATING_POINT | 
|  | 449 | case 'L': | 
|  | 450 | flags |= LONGDBL; | 
|  | 451 | goto rflag; | 
|  | 452 | #endif | 
|  | 453 | case 'h': | 
| Elliott Hughes | 1d13c64 | 2013-09-23 16:02:39 -0700 | [diff] [blame] | 454 | if (*fmt == 'h') { | 
|  | 455 | fmt++; | 
|  | 456 | flags |= CHARINT; | 
|  | 457 | } else { | 
|  | 458 | flags |= SHORTINT; | 
|  | 459 | } | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 460 | goto rflag; | 
|  | 461 | case 'j': | 
|  | 462 | flags |= MAXINT; | 
|  | 463 | goto rflag; | 
|  | 464 | case 'l': | 
|  | 465 | if (*fmt == 'l') { | 
|  | 466 | fmt++; | 
|  | 467 | flags |= LLONGINT; | 
|  | 468 | } else { | 
|  | 469 | flags |= LONGINT; | 
|  | 470 | } | 
|  | 471 | goto rflag; | 
|  | 472 | case 'q': | 
|  | 473 | flags |= LLONGINT; | 
|  | 474 | goto rflag; | 
|  | 475 | case 't': | 
|  | 476 | flags |= PTRINT; | 
|  | 477 | goto rflag; | 
|  | 478 | case 'z': | 
|  | 479 | flags |= SIZEINT; | 
|  | 480 | goto rflag; | 
|  | 481 | case 'c': | 
|  | 482 | *(cp = buf) = GETARG(int); | 
|  | 483 | size = 1; | 
|  | 484 | sign = '\0'; | 
|  | 485 | break; | 
|  | 486 | case 'D': | 
|  | 487 | flags |= LONGINT; | 
|  | 488 | /*FALLTHROUGH*/ | 
|  | 489 | case 'd': | 
|  | 490 | case 'i': | 
|  | 491 | _umax = SARG(); | 
|  | 492 | if ((intmax_t)_umax < 0) { | 
|  | 493 | _umax = -_umax; | 
|  | 494 | sign = '-'; | 
|  | 495 | } | 
|  | 496 | base = DEC; | 
|  | 497 | goto number; | 
|  | 498 | #ifdef FLOATING_POINT | 
|  | 499 | case 'e': | 
|  | 500 | case 'E': | 
|  | 501 | case 'f': | 
|  | 502 | case 'g': | 
|  | 503 | case 'G': | 
|  | 504 | if (prec == -1) { | 
|  | 505 | prec = DEFPREC; | 
|  | 506 | } else if ((ch == 'g' || ch == 'G') && prec == 0) { | 
|  | 507 | prec = 1; | 
|  | 508 | } | 
|  | 509 |  | 
|  | 510 | if (flags & LONGDBL) { | 
|  | 511 | _double = (double) GETARG(long double); | 
|  | 512 | } else { | 
|  | 513 | _double = GETARG(double); | 
|  | 514 | } | 
|  | 515 |  | 
|  | 516 | /* do this before tricky precision changes */ | 
|  | 517 | if (_my_isinf(_double)) { | 
|  | 518 | if (_double < 0) | 
|  | 519 | sign = '-'; | 
|  | 520 | cp = "Inf"; | 
|  | 521 | size = 3; | 
|  | 522 | break; | 
|  | 523 | } | 
|  | 524 | if (_my_isnan(_double)) { | 
|  | 525 | cp = "NaN"; | 
|  | 526 | size = 3; | 
|  | 527 | break; | 
|  | 528 | } | 
|  | 529 |  | 
|  | 530 | flags |= FPT; | 
|  | 531 | cp = cvt(_double, prec, flags, &softsign, | 
|  | 532 | &expt, ch, &ndig); | 
|  | 533 | cp_free = cp; | 
|  | 534 | if (ch == 'g' || ch == 'G') { | 
|  | 535 | if (expt <= -4 || expt > prec) | 
|  | 536 | ch = (ch == 'g') ? 'e' : 'E'; | 
|  | 537 | else | 
|  | 538 | ch = 'g'; | 
|  | 539 | } | 
|  | 540 | if (ch <= 'e') {	/* 'e' or 'E' fmt */ | 
|  | 541 | --expt; | 
|  | 542 | expsize = exponent(expstr, expt, ch); | 
|  | 543 | size = expsize + ndig; | 
|  | 544 | if (ndig > 1 || flags & ALT) | 
|  | 545 | ++size; | 
|  | 546 | } else if (ch == 'f') {		/* f fmt */ | 
|  | 547 | if (expt > 0) { | 
|  | 548 | size = expt; | 
|  | 549 | if (prec || flags & ALT) | 
|  | 550 | size += prec + 1; | 
|  | 551 | } else	/* "0.X" */ | 
|  | 552 | size = prec + 2; | 
|  | 553 | } else if (expt >= ndig) {	/* fixed g fmt */ | 
|  | 554 | size = expt; | 
|  | 555 | if (flags & ALT) | 
|  | 556 | ++size; | 
|  | 557 | } else | 
|  | 558 | size = ndig + (expt > 0 ? | 
|  | 559 | 1 : 2 - expt); | 
|  | 560 |  | 
|  | 561 | if (softsign) | 
|  | 562 | sign = '-'; | 
|  | 563 | break; | 
|  | 564 | #endif /* FLOATING_POINT */ | 
|  | 565 | /* the Android security team suggests removing support for %n | 
|  | 566 | * since it has no real practical value, and could lead to | 
| Nick Kralevich | 9145ad3 | 2012-07-25 16:01:38 -0700 | [diff] [blame] | 567 | * running malicious code (for really buggy programs that | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 568 | * send to printf() user-generated formatting strings). | 
|  | 569 | */ | 
|  | 570 | #if 0 | 
|  | 571 | case 'n': | 
|  | 572 | if (flags & LLONGINT) | 
|  | 573 | *GETARG(long long *) = ret; | 
|  | 574 | else if (flags & LONGINT) | 
|  | 575 | *GETARG(long *) = ret; | 
|  | 576 | else if (flags & SHORTINT) | 
|  | 577 | *GETARG(short *) = ret; | 
|  | 578 | else if (flags & CHARINT) | 
|  | 579 | *GETARG(__signed char *) = ret; | 
|  | 580 | else if (flags & PTRINT) | 
|  | 581 | *GETARG(ptrdiff_t *) = ret; | 
|  | 582 | else if (flags & SIZEINT) | 
|  | 583 | *GETARG(ssize_t *) = ret; | 
|  | 584 | else if (flags & MAXINT) | 
|  | 585 | *GETARG(intmax_t *) = ret; | 
|  | 586 | else | 
|  | 587 | *GETARG(int *) = ret; | 
|  | 588 | continue;	/* no output */ | 
|  | 589 | #endif | 
|  | 590 | case 'O': | 
|  | 591 | flags |= LONGINT; | 
|  | 592 | /*FALLTHROUGH*/ | 
|  | 593 | case 'o': | 
|  | 594 | _umax = UARG(); | 
|  | 595 | base = OCT; | 
|  | 596 | goto nosign; | 
|  | 597 | case 'p': | 
|  | 598 | /* | 
|  | 599 | * ``The argument shall be a pointer to void.  The | 
|  | 600 | * value of the pointer is converted to a sequence | 
|  | 601 | * of printable characters, in an implementation- | 
|  | 602 | * defined manner.'' | 
|  | 603 | *	-- ANSI X3J11 | 
|  | 604 | */ | 
|  | 605 | /* NOSTRICT */ | 
|  | 606 | _umax = (u_long)GETARG(void *); | 
|  | 607 | base = HEX; | 
|  | 608 | xdigs = "0123456789abcdef"; | 
|  | 609 | flags |= HEXPREFIX; | 
|  | 610 | ch = 'x'; | 
|  | 611 | goto nosign; | 
|  | 612 | case 's': | 
|  | 613 | if ((cp = GETARG(char *)) == NULL) | 
|  | 614 | cp = "(null)"; | 
|  | 615 | if (prec >= 0) { | 
|  | 616 | /* | 
|  | 617 | * can't use strlen; can only look for the | 
|  | 618 | * NUL in the first `prec' characters, and | 
|  | 619 | * strlen() will go further. | 
|  | 620 | */ | 
|  | 621 | char *p = memchr(cp, 0, prec); | 
|  | 622 |  | 
|  | 623 | if (p != NULL) { | 
|  | 624 | size = p - cp; | 
|  | 625 | if (size > prec) | 
|  | 626 | size = prec; | 
|  | 627 | } else | 
|  | 628 | size = prec; | 
|  | 629 | } else | 
|  | 630 | size = strlen(cp); | 
|  | 631 | sign = '\0'; | 
|  | 632 | break; | 
|  | 633 | case 'U': | 
|  | 634 | flags |= LONGINT; | 
|  | 635 | /*FALLTHROUGH*/ | 
|  | 636 | case 'u': | 
|  | 637 | _umax = UARG(); | 
|  | 638 | base = DEC; | 
|  | 639 | goto nosign; | 
|  | 640 | case 'X': | 
|  | 641 | xdigs = "0123456789ABCDEF"; | 
|  | 642 | goto hex; | 
|  | 643 | case 'x': | 
|  | 644 | xdigs = "0123456789abcdef"; | 
|  | 645 | hex:			_umax = UARG(); | 
|  | 646 | base = HEX; | 
|  | 647 | /* leading 0x/X only if non-zero */ | 
|  | 648 | if (flags & ALT && _umax != 0) | 
|  | 649 | flags |= HEXPREFIX; | 
|  | 650 |  | 
|  | 651 | /* unsigned conversions */ | 
|  | 652 | nosign:			sign = '\0'; | 
|  | 653 | /* | 
|  | 654 | * ``... diouXx conversions ... if a precision is | 
|  | 655 | * specified, the 0 flag will be ignored.'' | 
|  | 656 | *	-- ANSI X3J11 | 
|  | 657 | */ | 
|  | 658 | number:			if ((dprec = prec) >= 0) | 
|  | 659 | flags &= ~ZEROPAD; | 
|  | 660 |  | 
|  | 661 | /* | 
|  | 662 | * ``The result of converting a zero value with an | 
|  | 663 | * explicit precision of zero is no characters.'' | 
|  | 664 | *	-- ANSI X3J11 | 
|  | 665 | */ | 
|  | 666 | cp = buf + BUF; | 
|  | 667 | if (_umax != 0 || prec != 0) { | 
|  | 668 | /* | 
|  | 669 | * Unsigned mod is hard, and unsigned mod | 
|  | 670 | * by a constant is easier than that by | 
|  | 671 | * a variable; hence this switch. | 
|  | 672 | */ | 
|  | 673 | switch (base) { | 
|  | 674 | case OCT: | 
|  | 675 | do { | 
|  | 676 | *--cp = to_char(_umax & 7); | 
|  | 677 | _umax >>= 3; | 
|  | 678 | } while (_umax); | 
|  | 679 | /* handle octal leading 0 */ | 
|  | 680 | if (flags & ALT && *cp != '0') | 
|  | 681 | *--cp = '0'; | 
|  | 682 | break; | 
|  | 683 |  | 
|  | 684 | case DEC: | 
|  | 685 | /* many numbers are 1 digit */ | 
|  | 686 | while (_umax >= 10) { | 
|  | 687 | *--cp = to_char(_umax % 10); | 
|  | 688 | _umax /= 10; | 
|  | 689 | } | 
|  | 690 | *--cp = to_char(_umax); | 
|  | 691 | break; | 
|  | 692 |  | 
|  | 693 | case HEX: | 
|  | 694 | do { | 
|  | 695 | *--cp = xdigs[_umax & 15]; | 
|  | 696 | _umax >>= 4; | 
|  | 697 | } while (_umax); | 
|  | 698 | break; | 
|  | 699 |  | 
|  | 700 | default: | 
|  | 701 | cp = "bug in vfprintf: bad base"; | 
|  | 702 | size = strlen(cp); | 
|  | 703 | goto skipsize; | 
|  | 704 | } | 
|  | 705 | } | 
|  | 706 | size = buf + BUF - cp; | 
|  | 707 | skipsize: | 
|  | 708 | break; | 
|  | 709 | default:	/* "%?" prints ?, unless ? is NUL */ | 
|  | 710 | if (ch == '\0') | 
|  | 711 | goto done; | 
|  | 712 | /* pretend it was %c with argument ch */ | 
|  | 713 | cp = buf; | 
|  | 714 | *cp = ch; | 
|  | 715 | size = 1; | 
|  | 716 | sign = '\0'; | 
|  | 717 | break; | 
|  | 718 | } | 
|  | 719 |  | 
|  | 720 | /* | 
|  | 721 | * All reasonable formats wind up here.  At this point, `cp' | 
|  | 722 | * points to a string which (if not flags&LADJUST) should be | 
|  | 723 | * padded out to `width' places.  If flags&ZEROPAD, it should | 
|  | 724 | * first be prefixed by any sign or other prefix; otherwise, | 
|  | 725 | * it should be blank padded before the prefix is emitted. | 
|  | 726 | * After any left-hand padding and prefixing, emit zeroes | 
|  | 727 | * required by a decimal [diouxX] precision, then print the | 
|  | 728 | * string proper, then emit zeroes required by any leftover | 
|  | 729 | * floating precision; finally, if LADJUST, pad with blanks. | 
|  | 730 | * | 
|  | 731 | * Compute actual size, so we know how much to pad. | 
|  | 732 | * size excludes decimal prec; realsz includes it. | 
|  | 733 | */ | 
|  | 734 | realsz = dprec > size ? dprec : size; | 
|  | 735 | if (sign) | 
|  | 736 | realsz++; | 
|  | 737 | else if (flags & HEXPREFIX) | 
|  | 738 | realsz+= 2; | 
|  | 739 |  | 
|  | 740 | /* right-adjusting blank padding */ | 
|  | 741 | if ((flags & (LADJUST|ZEROPAD)) == 0) | 
|  | 742 | PAD(width - realsz, blanks); | 
|  | 743 |  | 
|  | 744 | /* prefix */ | 
|  | 745 | if (sign) { | 
|  | 746 | PRINT(&sign, 1); | 
|  | 747 | } else if (flags & HEXPREFIX) { | 
|  | 748 | ox[0] = '0'; | 
|  | 749 | ox[1] = ch; | 
|  | 750 | PRINT(ox, 2); | 
|  | 751 | } | 
|  | 752 |  | 
|  | 753 | /* right-adjusting zero padding */ | 
|  | 754 | if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) | 
|  | 755 | PAD(width - realsz, zeroes); | 
|  | 756 |  | 
|  | 757 | /* leading zeroes from decimal precision */ | 
|  | 758 | PAD(dprec - size, zeroes); | 
|  | 759 |  | 
|  | 760 | /* the string or number proper */ | 
|  | 761 | #ifdef FLOATING_POINT | 
|  | 762 | if ((flags & FPT) == 0) { | 
|  | 763 | PRINT(cp, size); | 
|  | 764 | } else {	/* glue together f_p fragments */ | 
|  | 765 | if (ch >= 'f') {	/* 'f' or 'g' */ | 
|  | 766 | if (_double == 0) { | 
|  | 767 | /* kludge for __dtoa irregularity */ | 
|  | 768 | PRINT("0", 1); | 
|  | 769 | if (expt < ndig || (flags & ALT) != 0) { | 
|  | 770 | PRINT(decimal_point, 1); | 
|  | 771 | PAD(ndig - 1, zeroes); | 
|  | 772 | } | 
|  | 773 | } else if (expt <= 0) { | 
|  | 774 | PRINT("0", 1); | 
|  | 775 | PRINT(decimal_point, 1); | 
|  | 776 | PAD(-expt, zeroes); | 
|  | 777 | PRINT(cp, ndig); | 
|  | 778 | } else if (expt >= ndig) { | 
|  | 779 | PRINT(cp, ndig); | 
|  | 780 | PAD(expt - ndig, zeroes); | 
|  | 781 | if (flags & ALT) | 
|  | 782 | PRINT(".", 1); | 
|  | 783 | } else { | 
|  | 784 | PRINT(cp, expt); | 
|  | 785 | cp += expt; | 
|  | 786 | PRINT(".", 1); | 
|  | 787 | PRINT(cp, ndig-expt); | 
|  | 788 | } | 
|  | 789 | } else {	/* 'e' or 'E' */ | 
|  | 790 | if (ndig > 1 || flags & ALT) { | 
|  | 791 | ox[0] = *cp++; | 
|  | 792 | ox[1] = '.'; | 
|  | 793 | PRINT(ox, 2); | 
|  | 794 | if (_double) { | 
|  | 795 | PRINT(cp, ndig-1); | 
|  | 796 | } else	/* 0.[0..] */ | 
|  | 797 | /* __dtoa irregularity */ | 
|  | 798 | PAD(ndig - 1, zeroes); | 
|  | 799 | } else	/* XeYYY */ | 
|  | 800 | PRINT(cp, 1); | 
|  | 801 | PRINT(expstr, expsize); | 
|  | 802 | } | 
|  | 803 | } | 
|  | 804 | #else | 
|  | 805 | PRINT(cp, size); | 
|  | 806 | #endif | 
|  | 807 | /* left-adjusting padding (always blank) */ | 
|  | 808 | if (flags & LADJUST) | 
|  | 809 | PAD(width - realsz, blanks); | 
|  | 810 |  | 
|  | 811 | /* finally, adjust ret */ | 
|  | 812 | ret += width > realsz ? width : realsz; | 
|  | 813 |  | 
|  | 814 | FLUSH();	/* copy out the I/O vectors */ | 
|  | 815 | #if 1   /* BIONIC: remove memory leak when printing doubles */ | 
|  | 816 | if (cp_free) { | 
|  | 817 | free(cp_free); | 
|  | 818 | cp_free = NULL; | 
|  | 819 | } | 
|  | 820 | #endif | 
|  | 821 | } | 
|  | 822 | done: | 
|  | 823 | FLUSH(); | 
|  | 824 | error: | 
|  | 825 | #if 1   /* BIONIC: remove memory leak when printing doubles */ | 
|  | 826 | if (cp_free) { | 
|  | 827 | free(cp_free); | 
|  | 828 | cp_free = NULL; | 
|  | 829 | } | 
|  | 830 | #endif | 
|  | 831 | if (argtable != NULL && argtable != statargtable) { | 
|  | 832 | munmap(argtable, argtablesiz); | 
|  | 833 | argtable = NULL; | 
|  | 834 | } | 
| Yaroslav Miroshnychenko | c7dcd67 | 2012-06-14 12:41:54 +0200 | [diff] [blame] | 835 | va_end(orgap); | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 836 | return (__sferror(fp) ? EOF : ret); | 
|  | 837 | /* NOTREACHED */ | 
|  | 838 | } | 
|  | 839 |  | 
|  | 840 | /* | 
|  | 841 | * Type ids for argument type table. | 
|  | 842 | */ | 
|  | 843 | #define T_UNUSED	0 | 
|  | 844 | #define T_SHORT		1 | 
|  | 845 | #define T_U_SHORT	2 | 
|  | 846 | #define TP_SHORT	3 | 
|  | 847 | #define T_INT		4 | 
|  | 848 | #define T_U_INT		5 | 
|  | 849 | #define TP_INT		6 | 
|  | 850 | #define T_LONG		7 | 
|  | 851 | #define T_U_LONG	8 | 
|  | 852 | #define TP_LONG		9 | 
|  | 853 | #define T_LLONG		10 | 
|  | 854 | #define T_U_LLONG	11 | 
|  | 855 | #define TP_LLONG	12 | 
|  | 856 | #define T_DOUBLE	13 | 
|  | 857 | #define T_LONG_DOUBLE	14 | 
|  | 858 | #define TP_CHAR		15 | 
|  | 859 | #define TP_VOID		16 | 
|  | 860 | #define T_PTRINT	17 | 
|  | 861 | #define TP_PTRINT	18 | 
|  | 862 | #define T_SIZEINT	19 | 
|  | 863 | #define T_SSIZEINT	20 | 
|  | 864 | #define TP_SSIZEINT	21 | 
|  | 865 | #define T_MAXINT	22 | 
|  | 866 | #define T_MAXUINT	23 | 
|  | 867 | #define TP_MAXINT	24 | 
|  | 868 |  | 
|  | 869 | /* | 
|  | 870 | * Find all arguments when a positional parameter is encountered.  Returns a | 
|  | 871 | * table, indexed by argument number, of pointers to each arguments.  The | 
|  | 872 | * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. | 
|  | 873 | * It will be replaced with a mmap-ed one if it overflows (malloc cannot be | 
|  | 874 | * used since we are attempting to make snprintf thread safe, and alloca is | 
|  | 875 | * problematic since we have nested functions..) | 
|  | 876 | */ | 
|  | 877 | static void | 
|  | 878 | __find_arguments(const char *fmt0, va_list ap, va_list **argtable, | 
|  | 879 | size_t *argtablesiz) | 
|  | 880 | { | 
|  | 881 | char *fmt;	/* format string */ | 
|  | 882 | int ch;	/* character from fmt */ | 
|  | 883 | int n, n2;	/* handy integer (short term usage) */ | 
|  | 884 | char *cp;	/* handy char pointer (short term usage) */ | 
|  | 885 | int flags;	/* flags as above */ | 
|  | 886 | unsigned char *typetable; /* table of types */ | 
|  | 887 | unsigned char stattypetable[STATIC_ARG_TBL_SIZE]; | 
|  | 888 | int tablesize;		/* current size of type table */ | 
|  | 889 | int tablemax;		/* largest used index in table */ | 
|  | 890 | int nextarg;		/* 1-based argument index */ | 
|  | 891 | wchar_t wc; | 
|  | 892 | void* ps; | 
|  | 893 |  | 
|  | 894 | /* | 
|  | 895 | * Add an argument type to the table, expanding if necessary. | 
|  | 896 | */ | 
|  | 897 | #define ADDTYPE(type) \ | 
|  | 898 | ((nextarg >= tablesize) ? \ | 
|  | 899 | __grow_type_table(&typetable, &tablesize) : 0, \ | 
|  | 900 | (nextarg > tablemax) ? tablemax = nextarg : 0, \ | 
|  | 901 | typetable[nextarg++] = type) | 
|  | 902 |  | 
|  | 903 | #define	ADDSARG() \ | 
|  | 904 | ((flags&MAXINT) ? ADDTYPE(T_MAXINT) : \ | 
|  | 905 | ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \ | 
|  | 906 | ((flags&SIZEINT) ? ADDTYPE(T_SSIZEINT) : \ | 
|  | 907 | ((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \ | 
|  | 908 | ((flags&LONGINT) ? ADDTYPE(T_LONG) : \ | 
|  | 909 | ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT))))))) | 
|  | 910 |  | 
|  | 911 | #define	ADDUARG() \ | 
|  | 912 | ((flags&MAXINT) ? ADDTYPE(T_MAXUINT) : \ | 
|  | 913 | ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \ | 
|  | 914 | ((flags&SIZEINT) ? ADDTYPE(T_SIZEINT) : \ | 
|  | 915 | ((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \ | 
|  | 916 | ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \ | 
|  | 917 | ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT))))))) | 
|  | 918 |  | 
|  | 919 | /* | 
|  | 920 | * Add * arguments to the type array. | 
|  | 921 | */ | 
|  | 922 | #define ADDASTER() \ | 
|  | 923 | n2 = 0; \ | 
|  | 924 | cp = fmt; \ | 
|  | 925 | while (is_digit(*cp)) { \ | 
|  | 926 | n2 = 10 * n2 + to_digit(*cp); \ | 
|  | 927 | cp++; \ | 
|  | 928 | } \ | 
|  | 929 | if (*cp == '$') { \ | 
|  | 930 | int hold = nextarg; \ | 
|  | 931 | nextarg = n2; \ | 
|  | 932 | ADDTYPE(T_INT); \ | 
|  | 933 | nextarg = hold; \ | 
|  | 934 | fmt = ++cp; \ | 
|  | 935 | } else { \ | 
|  | 936 | ADDTYPE(T_INT); \ | 
|  | 937 | } | 
|  | 938 | fmt = (char *)fmt0; | 
|  | 939 | typetable = stattypetable; | 
|  | 940 | tablesize = STATIC_ARG_TBL_SIZE; | 
|  | 941 | tablemax = 0; | 
|  | 942 | nextarg = 1; | 
|  | 943 | memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); | 
|  | 944 | memset(&ps, 0, sizeof(ps)); | 
|  | 945 |  | 
|  | 946 | /* | 
|  | 947 | * Scan the format for conversions (`%' character). | 
|  | 948 | */ | 
|  | 949 | for (;;) { | 
|  | 950 | cp = fmt; | 
|  | 951 | #if 1  /* BIONIC */ | 
|  | 952 | n = -1; | 
|  | 953 | while ((wc = *fmt) != 0) { | 
|  | 954 | if (wc == '%') { | 
|  | 955 | n = 1; | 
|  | 956 | break; | 
|  | 957 | } | 
|  | 958 | fmt++; | 
|  | 959 | } | 
|  | 960 | #else | 
|  | 961 | while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0) { | 
|  | 962 | fmt += n; | 
|  | 963 | if (wc == '%') { | 
|  | 964 | fmt--; | 
|  | 965 | break; | 
|  | 966 | } | 
|  | 967 | } | 
|  | 968 | #endif | 
|  | 969 | if (n <= 0) | 
|  | 970 | goto done; | 
|  | 971 | fmt++;		/* skip over '%' */ | 
|  | 972 |  | 
|  | 973 | flags = 0; | 
|  | 974 |  | 
|  | 975 | rflag:		ch = *fmt++; | 
|  | 976 | reswitch:	switch (ch) { | 
|  | 977 | case ' ': | 
|  | 978 | case '#': | 
|  | 979 | goto rflag; | 
|  | 980 | case '*': | 
|  | 981 | ADDASTER(); | 
|  | 982 | goto rflag; | 
|  | 983 | case '-': | 
|  | 984 | case '+': | 
|  | 985 | goto rflag; | 
|  | 986 | case '.': | 
|  | 987 | if ((ch = *fmt++) == '*') { | 
|  | 988 | ADDASTER(); | 
|  | 989 | goto rflag; | 
|  | 990 | } | 
|  | 991 | while (is_digit(ch)) { | 
|  | 992 | ch = *fmt++; | 
|  | 993 | } | 
|  | 994 | goto reswitch; | 
|  | 995 | case '0': | 
|  | 996 | goto rflag; | 
|  | 997 | case '1': case '2': case '3': case '4': | 
|  | 998 | case '5': case '6': case '7': case '8': case '9': | 
|  | 999 | n = 0; | 
|  | 1000 | do { | 
|  | 1001 | n = 10 * n + to_digit(ch); | 
|  | 1002 | ch = *fmt++; | 
|  | 1003 | } while (is_digit(ch)); | 
|  | 1004 | if (ch == '$') { | 
|  | 1005 | nextarg = n; | 
|  | 1006 | goto rflag; | 
|  | 1007 | } | 
|  | 1008 | goto reswitch; | 
|  | 1009 | #ifdef FLOATING_POINT | 
|  | 1010 | case 'L': | 
|  | 1011 | flags |= LONGDBL; | 
|  | 1012 | goto rflag; | 
|  | 1013 | #endif | 
|  | 1014 | case 'h': | 
|  | 1015 | if (*fmt == 'h') { | 
|  | 1016 | fmt++; | 
|  | 1017 | flags |= CHARINT; | 
|  | 1018 | } else { | 
|  | 1019 | flags |= SHORTINT; | 
|  | 1020 | } | 
|  | 1021 | goto rflag; | 
|  | 1022 | case 'l': | 
|  | 1023 | if (*fmt == 'l') { | 
|  | 1024 | fmt++; | 
|  | 1025 | flags |= LLONGINT; | 
|  | 1026 | } else { | 
|  | 1027 | flags |= LONGINT; | 
|  | 1028 | } | 
|  | 1029 | goto rflag; | 
|  | 1030 | case 'q': | 
|  | 1031 | flags |= LLONGINT; | 
|  | 1032 | goto rflag; | 
|  | 1033 | case 't': | 
|  | 1034 | flags |= PTRINT; | 
|  | 1035 | goto rflag; | 
|  | 1036 | case 'z': | 
|  | 1037 | flags |= SIZEINT; | 
|  | 1038 | goto rflag; | 
|  | 1039 | case 'c': | 
|  | 1040 | ADDTYPE(T_INT); | 
|  | 1041 | break; | 
|  | 1042 | case 'D': | 
|  | 1043 | flags |= LONGINT; | 
|  | 1044 | /*FALLTHROUGH*/ | 
|  | 1045 | case 'd': | 
|  | 1046 | case 'i': | 
|  | 1047 | ADDSARG(); | 
|  | 1048 | break; | 
|  | 1049 | #ifdef FLOATING_POINT | 
|  | 1050 | case 'e': | 
|  | 1051 | case 'E': | 
|  | 1052 | case 'f': | 
|  | 1053 | case 'g': | 
|  | 1054 | case 'G': | 
|  | 1055 | if (flags & LONGDBL) | 
|  | 1056 | ADDTYPE(T_LONG_DOUBLE); | 
|  | 1057 | else | 
|  | 1058 | ADDTYPE(T_DOUBLE); | 
|  | 1059 | break; | 
|  | 1060 | #endif /* FLOATING_POINT */ | 
|  | 1061 | case 'n': | 
|  | 1062 | if (flags & LLONGINT) | 
|  | 1063 | ADDTYPE(TP_LLONG); | 
|  | 1064 | else if (flags & LONGINT) | 
|  | 1065 | ADDTYPE(TP_LONG); | 
|  | 1066 | else if (flags & SHORTINT) | 
|  | 1067 | ADDTYPE(TP_SHORT); | 
|  | 1068 | else if (flags & PTRINT) | 
|  | 1069 | ADDTYPE(TP_PTRINT); | 
|  | 1070 | else if (flags & SIZEINT) | 
|  | 1071 | ADDTYPE(TP_SSIZEINT); | 
|  | 1072 | else if (flags & MAXINT) | 
|  | 1073 | ADDTYPE(TP_MAXINT); | 
|  | 1074 | else | 
|  | 1075 | ADDTYPE(TP_INT); | 
|  | 1076 | continue;	/* no output */ | 
|  | 1077 | case 'O': | 
|  | 1078 | flags |= LONGINT; | 
|  | 1079 | /*FALLTHROUGH*/ | 
|  | 1080 | case 'o': | 
|  | 1081 | ADDUARG(); | 
|  | 1082 | break; | 
|  | 1083 | case 'p': | 
|  | 1084 | ADDTYPE(TP_VOID); | 
|  | 1085 | break; | 
|  | 1086 | case 's': | 
|  | 1087 | ADDTYPE(TP_CHAR); | 
|  | 1088 | break; | 
|  | 1089 | case 'U': | 
|  | 1090 | flags |= LONGINT; | 
|  | 1091 | /*FALLTHROUGH*/ | 
|  | 1092 | case 'u': | 
|  | 1093 | case 'X': | 
|  | 1094 | case 'x': | 
|  | 1095 | ADDUARG(); | 
|  | 1096 | break; | 
|  | 1097 | default:	/* "%?" prints ?, unless ? is NUL */ | 
|  | 1098 | if (ch == '\0') | 
|  | 1099 | goto done; | 
|  | 1100 | break; | 
|  | 1101 | } | 
|  | 1102 | } | 
|  | 1103 | done: | 
|  | 1104 | /* | 
|  | 1105 | * Build the argument table. | 
|  | 1106 | */ | 
|  | 1107 | if (tablemax >= STATIC_ARG_TBL_SIZE) { | 
|  | 1108 | *argtablesiz = sizeof (va_list) * (tablemax + 1); | 
|  | 1109 | *argtable = (va_list *)mmap(NULL, *argtablesiz, | 
|  | 1110 | PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0); | 
|  | 1111 | } | 
|  | 1112 |  | 
|  | 1113 | #if 0 | 
|  | 1114 | /* XXX is this required? */ | 
|  | 1115 | (*argtable) [0] = NULL; | 
|  | 1116 | #endif | 
|  | 1117 | for (n = 1; n <= tablemax; n++) { | 
|  | 1118 | va_copy((*argtable)[n], ap); | 
|  | 1119 | switch (typetable[n]) { | 
|  | 1120 | case T_UNUSED: | 
|  | 1121 | (void) va_arg(ap, int); | 
|  | 1122 | break; | 
|  | 1123 | case T_SHORT: | 
|  | 1124 | (void) va_arg(ap, int); | 
|  | 1125 | break; | 
|  | 1126 | case T_U_SHORT: | 
|  | 1127 | (void) va_arg(ap, int); | 
|  | 1128 | break; | 
|  | 1129 | case TP_SHORT: | 
|  | 1130 | (void) va_arg(ap, short *); | 
|  | 1131 | break; | 
|  | 1132 | case T_INT: | 
|  | 1133 | (void) va_arg(ap, int); | 
|  | 1134 | break; | 
|  | 1135 | case T_U_INT: | 
|  | 1136 | (void) va_arg(ap, unsigned int); | 
|  | 1137 | break; | 
|  | 1138 | case TP_INT: | 
|  | 1139 | (void) va_arg(ap, int *); | 
|  | 1140 | break; | 
|  | 1141 | case T_LONG: | 
|  | 1142 | (void) va_arg(ap, long); | 
|  | 1143 | break; | 
|  | 1144 | case T_U_LONG: | 
|  | 1145 | (void) va_arg(ap, unsigned long); | 
|  | 1146 | break; | 
|  | 1147 | case TP_LONG: | 
|  | 1148 | (void) va_arg(ap, long *); | 
|  | 1149 | break; | 
|  | 1150 | case T_LLONG: | 
|  | 1151 | (void) va_arg(ap, long long); | 
|  | 1152 | break; | 
|  | 1153 | case T_U_LLONG: | 
|  | 1154 | (void) va_arg(ap, unsigned long long); | 
|  | 1155 | break; | 
|  | 1156 | case TP_LLONG: | 
|  | 1157 | (void) va_arg(ap, long long *); | 
|  | 1158 | break; | 
|  | 1159 | case T_DOUBLE: | 
|  | 1160 | (void) va_arg(ap, double); | 
|  | 1161 | break; | 
|  | 1162 | case T_LONG_DOUBLE: | 
|  | 1163 | (void) va_arg(ap, long double); | 
|  | 1164 | break; | 
|  | 1165 | case TP_CHAR: | 
|  | 1166 | (void) va_arg(ap, char *); | 
|  | 1167 | break; | 
|  | 1168 | case TP_VOID: | 
|  | 1169 | (void) va_arg(ap, void *); | 
|  | 1170 | break; | 
|  | 1171 | case T_PTRINT: | 
|  | 1172 | (void) va_arg(ap, ptrdiff_t); | 
|  | 1173 | break; | 
|  | 1174 | case TP_PTRINT: | 
|  | 1175 | (void) va_arg(ap, ptrdiff_t *); | 
|  | 1176 | break; | 
|  | 1177 | case T_SIZEINT: | 
|  | 1178 | (void) va_arg(ap, size_t); | 
|  | 1179 | break; | 
|  | 1180 | case T_SSIZEINT: | 
|  | 1181 | (void) va_arg(ap, ssize_t); | 
|  | 1182 | break; | 
|  | 1183 | case TP_SSIZEINT: | 
|  | 1184 | (void) va_arg(ap, ssize_t *); | 
|  | 1185 | break; | 
|  | 1186 | case TP_MAXINT: | 
|  | 1187 | (void) va_arg(ap, intmax_t *); | 
|  | 1188 | break; | 
|  | 1189 | } | 
|  | 1190 | } | 
|  | 1191 |  | 
|  | 1192 | if (typetable != NULL && typetable != stattypetable) { | 
|  | 1193 | munmap(typetable, *argtablesiz); | 
|  | 1194 | typetable = NULL; | 
|  | 1195 | } | 
|  | 1196 | } | 
|  | 1197 |  | 
|  | 1198 | /* | 
|  | 1199 | * Increase the size of the type table. | 
|  | 1200 | */ | 
|  | 1201 | static int | 
|  | 1202 | __grow_type_table(unsigned char **typetable, int *tablesize) | 
|  | 1203 | { | 
|  | 1204 | unsigned char *oldtable = *typetable; | 
|  | 1205 | int newsize = *tablesize * 2; | 
|  | 1206 |  | 
|  | 1207 | if (*tablesize == STATIC_ARG_TBL_SIZE) { | 
|  | 1208 | *typetable = (unsigned char *)mmap(NULL, | 
|  | 1209 | sizeof (unsigned char) * newsize, PROT_WRITE|PROT_READ, | 
|  | 1210 | MAP_ANON|MAP_PRIVATE, -1, 0); | 
|  | 1211 | /* XXX unchecked */ | 
|  | 1212 | memcpy( *typetable, oldtable, *tablesize); | 
|  | 1213 | } else { | 
|  | 1214 | unsigned char *new = (unsigned char *)mmap(NULL, | 
|  | 1215 | sizeof (unsigned char) * newsize, PROT_WRITE|PROT_READ, | 
|  | 1216 | MAP_ANON|MAP_PRIVATE, -1, 0); | 
|  | 1217 | memmove(new, *typetable, *tablesize); | 
|  | 1218 | munmap(*typetable, *tablesize); | 
|  | 1219 | *typetable = new; | 
|  | 1220 | /* XXX unchecked */ | 
|  | 1221 | } | 
|  | 1222 | memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize)); | 
|  | 1223 |  | 
|  | 1224 | *tablesize = newsize; | 
|  | 1225 | return(0); | 
|  | 1226 | } | 
|  | 1227 |  | 
|  | 1228 |  | 
|  | 1229 | #ifdef FLOATING_POINT | 
|  | 1230 |  | 
|  | 1231 | extern char *__dtoa(double, int, int, int *, int *, char **); | 
|  | 1232 |  | 
|  | 1233 | static char * | 
|  | 1234 | cvt(double value, int ndigits, int flags, char *sign, int *decpt, int ch, | 
|  | 1235 | int *length) | 
|  | 1236 | { | 
|  | 1237 | int mode, dsgn; | 
|  | 1238 | char *digits, *bp, *rve; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 1239 |  | 
|  | 1240 | if (ch == 'f') { | 
|  | 1241 | mode = 3;		/* ndigits after the decimal point */ | 
|  | 1242 | } else { | 
|  | 1243 | /* To obtain ndigits after the decimal point for the 'e' | 
|  | 1244 | * and 'E' formats, round to ndigits + 1 significant | 
|  | 1245 | * figures. | 
|  | 1246 | */ | 
|  | 1247 | if (ch == 'e' || ch == 'E') { | 
|  | 1248 | ndigits++; | 
|  | 1249 | } | 
|  | 1250 | mode = 2;		/* ndigits significant digits */ | 
|  | 1251 | } | 
|  | 1252 |  | 
|  | 1253 | if (value < 0) { | 
|  | 1254 | value = -value; | 
|  | 1255 | *sign = '-'; | 
|  | 1256 | } else | 
|  | 1257 | *sign = '\000'; | 
|  | 1258 | digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve); | 
|  | 1259 | if ((ch != 'g' && ch != 'G') || flags & ALT) {	/* Print trailing zeros */ | 
|  | 1260 | bp = digits + ndigits; | 
|  | 1261 | if (ch == 'f') { | 
|  | 1262 | if (*digits == '0' && value) | 
|  | 1263 | *decpt = -ndigits + 1; | 
|  | 1264 | bp += *decpt; | 
|  | 1265 | } | 
|  | 1266 | if (value == 0)	/* kludge for __dtoa irregularity */ | 
|  | 1267 | rve = bp; | 
|  | 1268 | while (rve < bp) | 
|  | 1269 | *rve++ = '0'; | 
|  | 1270 | } | 
|  | 1271 | *length = rve - digits; | 
|  | 1272 | return (digits); | 
|  | 1273 | } | 
|  | 1274 |  | 
|  | 1275 | static int | 
|  | 1276 | exponent(char *p0, int exp, int fmtch) | 
|  | 1277 | { | 
|  | 1278 | char *p, *t; | 
|  | 1279 | char expbuf[MAXEXP]; | 
|  | 1280 |  | 
|  | 1281 | p = p0; | 
|  | 1282 | *p++ = fmtch; | 
|  | 1283 | if (exp < 0) { | 
|  | 1284 | exp = -exp; | 
|  | 1285 | *p++ = '-'; | 
|  | 1286 | } | 
|  | 1287 | else | 
|  | 1288 | *p++ = '+'; | 
|  | 1289 | t = expbuf + MAXEXP; | 
|  | 1290 | if (exp > 9) { | 
|  | 1291 | do { | 
|  | 1292 | *--t = to_char(exp % 10); | 
|  | 1293 | } while ((exp /= 10) > 9); | 
|  | 1294 | *--t = to_char(exp); | 
|  | 1295 | for (; t < expbuf + MAXEXP; *p++ = *t++); | 
|  | 1296 | } | 
|  | 1297 | else { | 
|  | 1298 | *p++ = '0'; | 
|  | 1299 | *p++ = to_char(exp); | 
|  | 1300 | } | 
|  | 1301 | return (p - p0); | 
|  | 1302 | } | 
|  | 1303 |  | 
|  | 1304 |  | 
|  | 1305 | /* BIONIC */ | 
|  | 1306 | #include <machine/ieee.h> | 
|  | 1307 | typedef union { | 
|  | 1308 | double              d; | 
|  | 1309 | struct ieee_double  i; | 
|  | 1310 | } ieee_u; | 
|  | 1311 |  | 
|  | 1312 | static int | 
|  | 1313 | _my_isinf (double  value) | 
|  | 1314 | { | 
|  | 1315 | ieee_u   u; | 
|  | 1316 |  | 
|  | 1317 | u.d = value; | 
|  | 1318 | return (u.i.dbl_exp == 2047 && u.i.dbl_frach == 0 && u.i.dbl_fracl == 0); | 
|  | 1319 | } | 
|  | 1320 |  | 
|  | 1321 | static int | 
|  | 1322 | _my_isnan (double  value) | 
|  | 1323 | { | 
|  | 1324 | ieee_u   u; | 
|  | 1325 |  | 
|  | 1326 | u.d = value; | 
|  | 1327 | return (u.i.dbl_exp == 2047 && (u.i.dbl_frach != 0 || u.i.dbl_fracl != 0)); | 
|  | 1328 | } | 
|  | 1329 | #endif /* FLOATING_POINT */ |