| Elliott Hughes | 506c6de | 2016-01-15 16:30:18 -0800 | [diff] [blame] | 1 | /*	$OpenBSD: vfwprintf.c,v 1.15 2015/12/28 22:08:18 mmcc Exp $ */ | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 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 |  | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 34 | #define CHAR_TYPE wchar_t | 
| Elliott Hughes | 93a1f8b | 2017-11-07 22:52:29 -0800 | [diff] [blame] | 35 | #define FUNCTION_NAME __vfwprintf | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 36 | #define CHAR_TYPE_STRLEN wcslen | 
 | 37 | #define CHAR_TYPE_STRNLEN wcsnlen | 
 | 38 | #define CHAR_TYPE_INF L"INF" | 
 | 39 | #define CHAR_TYPE_inf L"inf" | 
 | 40 | #define CHAR_TYPE_NAN L"NAN" | 
 | 41 | #define CHAR_TYPE_nan L"nan" | 
 | 42 | #define CHAR_TYPE_ORIENTATION 1 | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 43 | #include "printf_common.h" | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 44 |  | 
| Elliott Hughes | 1f49317 | 2017-11-08 16:13:18 -0800 | [diff] [blame] | 45 | int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) { | 
| Elliott Hughes | b70576b | 2017-11-13 11:10:05 -0800 | [diff] [blame] | 46 |   int n, n2; | 
| Elliott Hughes | 93a1f8b | 2017-11-07 22:52:29 -0800 | [diff] [blame] | 47 |   CHAR_TYPE* cp;   /* handy char pointer (short term usage) */ | 
 | 48 |   CHAR_TYPE sign;  /* sign prefix (' ', '+', '-', or \0) */ | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 49 |   int flags;     /* flags as above */ | 
 | 50 |   int ret;       /* return value accumulator */ | 
 | 51 |   int width;     /* width from format (%8d), or 0 */ | 
 | 52 |   int prec;      /* precision from format; <0 for N/A */ | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 53 |   /* | 
 | 54 |    * We can decompose the printed representation of floating | 
 | 55 |    * point numbers into several parts, some of which may be empty: | 
 | 56 |    * | 
 | 57 |    * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ | 
 | 58 |    *    A       B     ---C---      D       E   F | 
 | 59 |    * | 
 | 60 |    * A:	'sign' holds this value if present; '\0' otherwise | 
 | 61 |    * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal | 
 | 62 |    * C:	cp points to the string MMMNNN.  Leading and trailing | 
 | 63 |    *	zeros are not in the string and must be added. | 
 | 64 |    * D:	expchar holds this character; '\0' if no exponent, e.g. %f | 
 | 65 |    * F:	at least two digits for decimal, at least one digit for hex | 
 | 66 |    */ | 
 | 67 |   char* decimal_point = NULL; | 
 | 68 |   int signflag; /* true if float is negative */ | 
 | 69 |   union {       /* floating point arguments %[aAeEfFgG] */ | 
 | 70 |     double dbl; | 
 | 71 |     long double ldbl; | 
 | 72 |   } fparg; | 
 | 73 |   int expt;                      /* integer value of exponent */ | 
 | 74 |   char expchar;                  /* exponent character: [eEpP\0] */ | 
 | 75 |   char* dtoaend;                 /* pointer to end of converted digits */ | 
 | 76 |   int expsize;                   /* character count for expstr */ | 
 | 77 |   int lead;                      /* sig figs before decimal or group sep */ | 
 | 78 |   int ndig;                      /* actual number of digits returned by dtoa */ | 
| Elliott Hughes | 93a1f8b | 2017-11-07 22:52:29 -0800 | [diff] [blame] | 79 |   CHAR_TYPE expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */ | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 80 |   char* dtoaresult = NULL; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 81 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 82 |   uintmax_t _umax;             /* integer arguments %[diouxX] */ | 
 | 83 |   enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */ | 
 | 84 |   int dprec;                   /* a copy of prec if %[diouxX], 0 otherwise */ | 
 | 85 |   int realsz;                  /* field size expanded by dprec */ | 
 | 86 |   int size;                    /* size of converted field or string */ | 
 | 87 |   const char* xdigs;           /* digits for %[xX] conversion */ | 
| Elliott Hughes | b70576b | 2017-11-13 11:10:05 -0800 | [diff] [blame] | 88 | #define NIOV 8 | 
 | 89 |   struct __suio uio;       /* output information: summary */ | 
 | 90 |   struct __siov iov[NIOV]; /* ... and individual io vectors */ | 
 | 91 |   struct __siov* iovp; /* for PRINT macro */ | 
| Elliott Hughes | 93a1f8b | 2017-11-07 22:52:29 -0800 | [diff] [blame] | 92 |   CHAR_TYPE buf[BUF];            /* buffer with space for digits of uintmax_t */ | 
 | 93 |   CHAR_TYPE ox[2];               /* space for 0x; ox[1] is either x, X, or \0 */ | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 94 |   union arg* argtable;         /* args, built due to positional arg */ | 
 | 95 |   union arg statargtable[STATIC_ARG_TBL_SIZE]; | 
 | 96 |   size_t argtablesiz; | 
 | 97 |   int nextarg;      /* 1-based argument index */ | 
 | 98 |   va_list orgap;    /* original argument pointer */ | 
| Elliott Hughes | 93a1f8b | 2017-11-07 22:52:29 -0800 | [diff] [blame] | 99 |   CHAR_TYPE* convbuf; /* buffer for wide/multibyte conversion */ | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 100 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 101 |   /* | 
 | 102 |    * Choose PADSIZE to trade efficiency vs. size.  If larger printf | 
 | 103 |    * fields occur frequently, increase PADSIZE and make the initialisers | 
 | 104 |    * below longer. | 
 | 105 |    */ | 
 | 106 | #define PADSIZE 16 /* pad chunk size */ | 
| Elliott Hughes | 5305a4d | 2017-11-03 14:00:37 -0700 | [diff] [blame] | 107 |   static CHAR_TYPE blanks[PADSIZE] = { | 
 | 108 |     ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' | 
 | 109 |   }; | 
 | 110 |   static CHAR_TYPE zeroes[PADSIZE] = { | 
 | 111 |     '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' | 
 | 112 |   }; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 113 |  | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 114 |   static const char xdigs_lower[] = "0123456789abcdef"; | 
 | 115 |   static const char xdigs_upper[] = "0123456789ABCDEF"; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 116 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 117 | #define PRINT(ptr, len)                                   \ | 
 | 118 |   do {                                                    \ | 
| Elliott Hughes | b70576b | 2017-11-13 11:10:05 -0800 | [diff] [blame] | 119 |     for (int n3 = 0; n3 < (len); n3++) {                      \ | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 120 |       if ((helpers::xfputwc((ptr)[n3], fp)) == WEOF) goto error; \ | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 121 |     }                                                     \ | 
 | 122 |   } while (0) | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 123 |  | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 124 |   _SET_ORIENTATION(fp, CHAR_TYPE_ORIENTATION); | 
| Elliott Hughes | 5305a4d | 2017-11-03 14:00:37 -0700 | [diff] [blame] | 125 |  | 
 | 126 |   // Writing "" to a read only file returns EOF, not 0. | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 127 |   if (cantwrite(fp)) { | 
 | 128 |     errno = EBADF; | 
| Elliott Hughes | 5305a4d | 2017-11-03 14:00:37 -0700 | [diff] [blame] | 129 |     return EOF; | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 130 |   } | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 131 |  | 
| Elliott Hughes | 5305a4d | 2017-11-03 14:00:37 -0700 | [diff] [blame] | 132 |   // Optimize writes to stderr and other unbuffered files). | 
 | 133 |   if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0) { | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 134 |     return (__sbprintf(fp, fmt0, ap)); | 
| Elliott Hughes | 5305a4d | 2017-11-03 14:00:37 -0700 | [diff] [blame] | 135 |   } | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 136 |  | 
| Elliott Hughes | 5305a4d | 2017-11-03 14:00:37 -0700 | [diff] [blame] | 137 |   CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 138 |   argtable = NULL; | 
 | 139 |   nextarg = 1; | 
 | 140 |   va_copy(orgap, ap); | 
| Elliott Hughes | b70576b | 2017-11-13 11:10:05 -0800 | [diff] [blame] | 141 |   uio.uio_iov = iovp = iov; | 
 | 142 |   uio.uio_resid = 0; | 
 | 143 |   uio.uio_iovcnt = 0; | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 144 |   ret = 0; | 
 | 145 |   convbuf = NULL; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 146 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 147 |   /* | 
 | 148 |    * Scan the format for conversions (`%' character). | 
 | 149 |    */ | 
 | 150 |   for (;;) { | 
| Elliott Hughes | b70576b | 2017-11-13 11:10:05 -0800 | [diff] [blame] | 151 |     int ch; | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 152 |     for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; | 
 | 153 |     if (fmt != cp) { | 
 | 154 |       ptrdiff_t m = fmt - cp; | 
 | 155 |       if (m < 0 || m > INT_MAX - ret) goto overflow; | 
 | 156 |       PRINT(cp, m); | 
 | 157 |       ret += m; | 
 | 158 |     } | 
 | 159 |     if (ch == '\0') goto done; | 
 | 160 |     fmt++; /* skip over '%' */ | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 161 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 162 |     flags = 0; | 
 | 163 |     dprec = 0; | 
 | 164 |     width = 0; | 
 | 165 |     prec = -1; | 
 | 166 |     sign = '\0'; | 
 | 167 |     ox[1] = '\0'; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 168 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 169 |   rflag: | 
 | 170 |     ch = *fmt++; | 
 | 171 |   reswitch: | 
 | 172 |     switch (ch) { | 
 | 173 |       case ' ': | 
 | 174 |         /* | 
 | 175 |          * ``If the space and + flags both appear, the space | 
 | 176 |          * flag will be ignored.'' | 
 | 177 |          *	-- ANSI X3J11 | 
 | 178 |          */ | 
 | 179 |         if (!sign) sign = ' '; | 
 | 180 |         goto rflag; | 
 | 181 |       case '#': | 
 | 182 |         flags |= ALT; | 
 | 183 |         goto rflag; | 
 | 184 |       case '\'': | 
 | 185 |         /* grouping not implemented */ | 
 | 186 |         goto rflag; | 
 | 187 |       case '*': | 
 | 188 |         /* | 
 | 189 |          * ``A negative field width argument is taken as a | 
 | 190 |          * - flag followed by a positive field width.'' | 
 | 191 |          *	-- ANSI X3J11 | 
 | 192 |          * They don't exclude field widths read from args. | 
 | 193 |          */ | 
 | 194 |         GETASTER(width); | 
 | 195 |         if (width >= 0) goto rflag; | 
 | 196 |         if (width == INT_MIN) goto overflow; | 
 | 197 |         width = -width; | 
 | 198 |         /* FALLTHROUGH */ | 
 | 199 |       case '-': | 
 | 200 |         flags |= LADJUST; | 
 | 201 |         goto rflag; | 
 | 202 |       case '+': | 
 | 203 |         sign = '+'; | 
 | 204 |         goto rflag; | 
 | 205 |       case '.': | 
 | 206 |         if ((ch = *fmt++) == '*') { | 
 | 207 |           GETASTER(n); | 
 | 208 |           prec = n < 0 ? -1 : n; | 
 | 209 |           goto rflag; | 
 | 210 |         } | 
 | 211 |         n = 0; | 
 | 212 |         while (is_digit(ch)) { | 
 | 213 |           APPEND_DIGIT(n, ch); | 
 | 214 |           ch = *fmt++; | 
 | 215 |         } | 
 | 216 |         if (ch == '$') { | 
 | 217 |           nextarg = n; | 
 | 218 |           if (argtable == NULL) { | 
 | 219 |             argtable = statargtable; | 
| Elliott Hughes | 5305a4d | 2017-11-03 14:00:37 -0700 | [diff] [blame] | 220 |             if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { | 
 | 221 |               ret = -1; | 
 | 222 |               goto error; | 
 | 223 |             } | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 224 |           } | 
 | 225 |           goto rflag; | 
 | 226 |         } | 
 | 227 |         prec = n; | 
 | 228 |         goto reswitch; | 
 | 229 |       case '0': | 
 | 230 |         /* | 
 | 231 |          * ``Note that 0 is taken as a flag, not as the | 
 | 232 |          * beginning of a field width.'' | 
 | 233 |          *	-- ANSI X3J11 | 
 | 234 |          */ | 
 | 235 |         flags |= ZEROPAD; | 
 | 236 |         goto rflag; | 
 | 237 |       case '1': | 
 | 238 |       case '2': | 
 | 239 |       case '3': | 
 | 240 |       case '4': | 
 | 241 |       case '5': | 
 | 242 |       case '6': | 
 | 243 |       case '7': | 
 | 244 |       case '8': | 
 | 245 |       case '9': | 
 | 246 |         n = 0; | 
 | 247 |         do { | 
 | 248 |           APPEND_DIGIT(n, ch); | 
 | 249 |           ch = *fmt++; | 
 | 250 |         } while (is_digit(ch)); | 
 | 251 |         if (ch == '$') { | 
 | 252 |           nextarg = n; | 
 | 253 |           if (argtable == NULL) { | 
 | 254 |             argtable = statargtable; | 
| Elliott Hughes | 5305a4d | 2017-11-03 14:00:37 -0700 | [diff] [blame] | 255 |             if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { | 
 | 256 |               ret = -1; | 
 | 257 |               goto error; | 
 | 258 |             } | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 259 |           } | 
 | 260 |           goto rflag; | 
 | 261 |         } | 
 | 262 |         width = n; | 
 | 263 |         goto reswitch; | 
 | 264 |       case 'L': | 
 | 265 |         flags |= LONGDBL; | 
 | 266 |         goto rflag; | 
 | 267 |       case 'h': | 
 | 268 |         if (*fmt == 'h') { | 
 | 269 |           fmt++; | 
 | 270 |           flags |= CHARINT; | 
 | 271 |         } else { | 
 | 272 |           flags |= SHORTINT; | 
 | 273 |         } | 
 | 274 |         goto rflag; | 
 | 275 |       case 'j': | 
 | 276 |         flags |= MAXINT; | 
 | 277 |         goto rflag; | 
 | 278 |       case 'l': | 
 | 279 |         if (*fmt == 'l') { | 
 | 280 |           fmt++; | 
 | 281 |           flags |= LLONGINT; | 
 | 282 |         } else { | 
 | 283 |           flags |= LONGINT; | 
 | 284 |         } | 
 | 285 |         goto rflag; | 
 | 286 |       case 'q': | 
 | 287 |         flags |= LLONGINT; | 
 | 288 |         goto rflag; | 
 | 289 |       case 't': | 
 | 290 |         flags |= PTRINT; | 
 | 291 |         goto rflag; | 
 | 292 |       case 'z': | 
 | 293 |         flags |= SIZEINT; | 
 | 294 |         goto rflag; | 
 | 295 |       case 'C': | 
 | 296 |         flags |= LONGINT; | 
 | 297 |         /*FALLTHROUGH*/ | 
 | 298 |       case 'c': | 
 | 299 |         if (flags & LONGINT) | 
 | 300 |           *(cp = buf) = (wchar_t)GETARG(wint_t); | 
 | 301 |         else | 
 | 302 |           *(cp = buf) = (wchar_t)btowc(GETARG(int)); | 
 | 303 |         size = 1; | 
 | 304 |         sign = '\0'; | 
 | 305 |         break; | 
 | 306 |       case 'D': | 
 | 307 |         flags |= LONGINT; | 
 | 308 |         /*FALLTHROUGH*/ | 
 | 309 |       case 'd': | 
 | 310 |       case 'i': | 
 | 311 |         _umax = SARG(); | 
 | 312 |         if ((intmax_t)_umax < 0) { | 
 | 313 |           _umax = -_umax; | 
 | 314 |           sign = '-'; | 
 | 315 |         } | 
 | 316 |         base = DEC; | 
 | 317 |         goto number; | 
 | 318 |       case 'a': | 
 | 319 |       case 'A': | 
 | 320 |         if (ch == 'a') { | 
 | 321 |           ox[1] = 'x'; | 
 | 322 |           xdigs = xdigs_lower; | 
 | 323 |           expchar = 'p'; | 
 | 324 |         } else { | 
 | 325 |           ox[1] = 'X'; | 
 | 326 |           xdigs = xdigs_upper; | 
 | 327 |           expchar = 'P'; | 
 | 328 |         } | 
 | 329 |         if (prec >= 0) prec++; | 
 | 330 |         if (dtoaresult) __freedtoa(dtoaresult); | 
 | 331 |         if (flags & LONGDBL) { | 
 | 332 |           fparg.ldbl = GETARG(long double); | 
 | 333 |           dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend); | 
 | 334 |           if (dtoaresult == NULL) { | 
 | 335 |             errno = ENOMEM; | 
 | 336 |             goto error; | 
 | 337 |           } | 
 | 338 |         } else { | 
 | 339 |           fparg.dbl = GETARG(double); | 
 | 340 |           dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend); | 
 | 341 |           if (dtoaresult == NULL) { | 
 | 342 |             errno = ENOMEM; | 
 | 343 |             goto error; | 
 | 344 |           } | 
 | 345 |         } | 
 | 346 |         if (prec < 0) prec = dtoaend - dtoaresult; | 
 | 347 |         if (expt == INT_MAX) ox[1] = '\0'; | 
 | 348 |         free(convbuf); | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 349 |         cp = convbuf = helpers::mbsconv(dtoaresult, -1); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 350 |         if (cp == NULL) goto error; | 
 | 351 |         ndig = dtoaend - dtoaresult; | 
 | 352 |         goto fp_common; | 
 | 353 |       case 'e': | 
 | 354 |       case 'E': | 
 | 355 |         expchar = ch; | 
 | 356 |         if (prec < 0) /* account for digit before decpt */ | 
 | 357 |           prec = DEFPREC + 1; | 
 | 358 |         else | 
 | 359 |           prec++; | 
 | 360 |         goto fp_begin; | 
 | 361 |       case 'f': | 
 | 362 |       case 'F': | 
 | 363 |         expchar = '\0'; | 
 | 364 |         goto fp_begin; | 
 | 365 |       case 'g': | 
 | 366 |       case 'G': | 
 | 367 |         expchar = ch - ('g' - 'e'); | 
 | 368 |         if (prec == 0) prec = 1; | 
 | 369 |       fp_begin: | 
 | 370 |         if (prec < 0) prec = DEFPREC; | 
 | 371 |         if (dtoaresult) __freedtoa(dtoaresult); | 
 | 372 |         if (flags & LONGDBL) { | 
 | 373 |           fparg.ldbl = GETARG(long double); | 
 | 374 |           dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); | 
 | 375 |           if (dtoaresult == NULL) { | 
 | 376 |             errno = ENOMEM; | 
 | 377 |             goto error; | 
 | 378 |           } | 
 | 379 |         } else { | 
 | 380 |           fparg.dbl = GETARG(double); | 
 | 381 |           dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); | 
 | 382 |           if (dtoaresult == NULL) { | 
 | 383 |             errno = ENOMEM; | 
 | 384 |             goto error; | 
 | 385 |           } | 
 | 386 |           if (expt == 9999) expt = INT_MAX; | 
 | 387 |         } | 
 | 388 |         free(convbuf); | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 389 |         cp = convbuf = helpers::mbsconv(dtoaresult, -1); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 390 |         if (cp == NULL) goto error; | 
 | 391 |         ndig = dtoaend - dtoaresult; | 
 | 392 |       fp_common: | 
 | 393 |         if (signflag) sign = '-'; | 
 | 394 |         if (expt == INT_MAX) { /* inf or nan */ | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 395 |           if (*cp == 'N') { | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 396 |             cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_nan : CHAR_TYPE_NAN); | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 397 |           } else { | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 398 |             cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_inf : CHAR_TYPE_INF); | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 399 |           } | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 400 |           size = 3; | 
 | 401 |           flags &= ~ZEROPAD; | 
 | 402 |           break; | 
 | 403 |         } | 
 | 404 |         flags |= FPT; | 
 | 405 |         if (ch == 'g' || ch == 'G') { | 
 | 406 |           if (expt > -4 && expt <= prec) { | 
 | 407 |             /* Make %[gG] smell like %[fF] */ | 
 | 408 |             expchar = '\0'; | 
 | 409 |             if (flags & ALT) | 
 | 410 |               prec -= expt; | 
 | 411 |             else | 
 | 412 |               prec = ndig - expt; | 
 | 413 |             if (prec < 0) prec = 0; | 
 | 414 |           } else { | 
 | 415 |             /* | 
 | 416 |              * Make %[gG] smell like %[eE], but | 
 | 417 |              * trim trailing zeroes if no # flag. | 
 | 418 |              */ | 
 | 419 |             if (!(flags & ALT)) prec = ndig; | 
 | 420 |           } | 
 | 421 |         } | 
 | 422 |         if (expchar) { | 
 | 423 |           expsize = exponent(expstr, expt - 1, expchar); | 
 | 424 |           size = expsize + prec; | 
 | 425 |           if (prec > 1 || flags & ALT) ++size; | 
 | 426 |         } else { | 
 | 427 |           /* space for digits before decimal point */ | 
 | 428 |           if (expt > 0) | 
 | 429 |             size = expt; | 
 | 430 |           else /* "0" */ | 
 | 431 |             size = 1; | 
 | 432 |           /* space for decimal pt and following digits */ | 
 | 433 |           if (prec || flags & ALT) size += prec + 1; | 
 | 434 |           lead = expt; | 
 | 435 |         } | 
 | 436 |         break; | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 437 |       case 'n': | 
| Elliott Hughes | 41398d0 | 2018-03-07 13:32:58 -0800 | [diff] [blame] | 438 |         __fortify_fatal("%%n not allowed on Android"); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 439 |       case 'O': | 
 | 440 |         flags |= LONGINT; | 
 | 441 |         /*FALLTHROUGH*/ | 
 | 442 |       case 'o': | 
 | 443 |         _umax = UARG(); | 
 | 444 |         base = OCT; | 
 | 445 |         goto nosign; | 
 | 446 |       case 'p': | 
 | 447 |         /* | 
 | 448 |          * ``The argument shall be a pointer to void.  The | 
 | 449 |          * value of the pointer is converted to a sequence | 
 | 450 |          * of printable characters, in an implementation- | 
 | 451 |          * defined manner.'' | 
 | 452 |          *	-- ANSI X3J11 | 
 | 453 |          */ | 
 | 454 |         _umax = (u_long)GETARG(void*); | 
 | 455 |         base = HEX; | 
 | 456 |         xdigs = xdigs_lower; | 
 | 457 |         ox[1] = 'x'; | 
 | 458 |         goto nosign; | 
 | 459 |       case 'S': | 
 | 460 |         flags |= LONGINT; | 
 | 461 |         /*FALLTHROUGH*/ | 
 | 462 |       case 's': | 
 | 463 |         if (flags & LONGINT) { | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 464 |           if ((cp = GETARG(wchar_t*)) == NULL) cp = const_cast<wchar_t*>(L"(null)"); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 465 |         } else { | 
 | 466 |           char* mbsarg; | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 467 |           if ((mbsarg = GETARG(char*)) == NULL) mbsarg = const_cast<char*>("(null)"); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 468 |           free(convbuf); | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 469 |           convbuf = helpers::mbsconv(mbsarg, prec); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 470 |           if (convbuf == NULL) { | 
 | 471 |             fp->_flags |= __SERR; | 
 | 472 |             goto error; | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 473 |           } else { | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 474 |             cp = convbuf; | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 475 |           } | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 476 |         } | 
 | 477 |         if (prec >= 0) { | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 478 |           size = CHAR_TYPE_STRNLEN(cp, prec); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 479 |         } else { | 
 | 480 |           size_t len; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 481 |  | 
| Elliott Hughes | bc27bdc | 2017-11-10 15:25:49 -0800 | [diff] [blame] | 482 |           if ((len = CHAR_TYPE_STRLEN(cp)) > INT_MAX) goto overflow; | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 483 |           size = (int)len; | 
 | 484 |         } | 
 | 485 |         sign = '\0'; | 
 | 486 |         break; | 
 | 487 |       case 'U': | 
 | 488 |         flags |= LONGINT; | 
 | 489 |         /*FALLTHROUGH*/ | 
 | 490 |       case 'u': | 
 | 491 |         _umax = UARG(); | 
 | 492 |         base = DEC; | 
 | 493 |         goto nosign; | 
 | 494 |       case 'X': | 
 | 495 |         xdigs = xdigs_upper; | 
 | 496 |         goto hex; | 
 | 497 |       case 'x': | 
 | 498 |         xdigs = xdigs_lower; | 
 | 499 |       hex: | 
 | 500 |         _umax = UARG(); | 
 | 501 |         base = HEX; | 
 | 502 |         /* leading 0x/X only if non-zero */ | 
 | 503 |         if (flags & ALT && _umax != 0) ox[1] = ch; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 504 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 505 |         /* unsigned conversions */ | 
 | 506 |       nosign: | 
 | 507 |         sign = '\0'; | 
 | 508 |         /* | 
 | 509 |          * ``... diouXx conversions ... if a precision is | 
 | 510 |          * specified, the 0 flag will be ignored.'' | 
 | 511 |          *	-- ANSI X3J11 | 
 | 512 |          */ | 
 | 513 |       number: | 
 | 514 |         if ((dprec = prec) >= 0) flags &= ~ZEROPAD; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 515 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 516 |         /* | 
 | 517 |          * ``The result of converting a zero value with an | 
 | 518 |          * explicit precision of zero is no characters.'' | 
 | 519 |          *	-- ANSI X3J11 | 
 | 520 |          */ | 
 | 521 |         cp = buf + BUF; | 
 | 522 |         if (_umax != 0 || prec != 0) { | 
 | 523 |           /* | 
 | 524 |            * Unsigned mod is hard, and unsigned mod | 
 | 525 |            * by a constant is easier than that by | 
 | 526 |            * a variable; hence this switch. | 
 | 527 |            */ | 
 | 528 |           switch (base) { | 
 | 529 |             case OCT: | 
 | 530 |               do { | 
 | 531 |                 *--cp = to_char(_umax & 7); | 
 | 532 |                 _umax >>= 3; | 
 | 533 |               } while (_umax); | 
 | 534 |               /* handle octal leading 0 */ | 
 | 535 |               if (flags & ALT && *cp != '0') *--cp = '0'; | 
 | 536 |               break; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 537 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 538 |             case DEC: | 
 | 539 |               /* many numbers are 1 digit */ | 
 | 540 |               while (_umax >= 10) { | 
 | 541 |                 *--cp = to_char(_umax % 10); | 
 | 542 |                 _umax /= 10; | 
 | 543 |               } | 
 | 544 |               *--cp = to_char(_umax); | 
 | 545 |               break; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 546 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 547 |             case HEX: | 
 | 548 |               do { | 
 | 549 |                 *--cp = xdigs[_umax & 15]; | 
 | 550 |                 _umax >>= 4; | 
 | 551 |               } while (_umax); | 
 | 552 |               break; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 553 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 554 |             default: | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 555 |               abort(); | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 556 |           } | 
 | 557 |         } | 
 | 558 |         size = buf + BUF - cp; | 
| Elliott Hughes | 2f9c8ce | 2017-11-01 13:54:47 -0700 | [diff] [blame] | 559 |         if (size > BUF) abort(); /* should never happen */ | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 560 |         break; | 
 | 561 |       default: /* "%?" prints ?, unless ? is NUL */ | 
 | 562 |         if (ch == '\0') goto done; | 
 | 563 |         /* pretend it was %c with argument ch */ | 
 | 564 |         cp = buf; | 
 | 565 |         *cp = ch; | 
 | 566 |         size = 1; | 
 | 567 |         sign = '\0'; | 
 | 568 |         break; | 
 | 569 |     } | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 570 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 571 |     /* | 
 | 572 |      * All reasonable formats wind up here.  At this point, `cp' | 
 | 573 |      * points to a string which (if not flags&LADJUST) should be | 
 | 574 |      * padded out to `width' places.  If flags&ZEROPAD, it should | 
 | 575 |      * first be prefixed by any sign or other prefix; otherwise, | 
 | 576 |      * it should be blank padded before the prefix is emitted. | 
 | 577 |      * After any left-hand padding and prefixing, emit zeroes | 
 | 578 |      * required by a decimal %[diouxX] precision, then print the | 
 | 579 |      * string proper, then emit zeroes required by any leftover | 
 | 580 |      * floating precision; finally, if LADJUST, pad with blanks. | 
 | 581 |      * | 
 | 582 |      * Compute actual size, so we know how much to pad. | 
 | 583 |      * size excludes decimal prec; realsz includes it. | 
 | 584 |      */ | 
 | 585 |     realsz = dprec > size ? dprec : size; | 
 | 586 |     if (sign) realsz++; | 
 | 587 |     if (ox[1]) realsz += 2; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 588 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 589 |     /* right-adjusting blank padding */ | 
 | 590 |     if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks); | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 591 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 592 |     /* prefix */ | 
 | 593 |     if (sign) PRINT(&sign, 1); | 
 | 594 |     if (ox[1]) { /* ox[1] is either x, X, or \0 */ | 
 | 595 |       ox[0] = '0'; | 
 | 596 |       PRINT(ox, 2); | 
 | 597 |     } | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 598 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 599 |     /* right-adjusting zero padding */ | 
 | 600 |     if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes); | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 601 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 602 |     /* leading zeroes from decimal precision */ | 
 | 603 |     PAD(dprec - size, zeroes); | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 604 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 605 |     /* the string or number proper */ | 
 | 606 |     if ((flags & FPT) == 0) { | 
 | 607 |       PRINT(cp, size); | 
 | 608 |     } else { /* glue together f_p fragments */ | 
 | 609 |       if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR); | 
 | 610 |       if (!expchar) { /* %[fF] or sufficiently short %[gG] */ | 
 | 611 |         if (expt <= 0) { | 
 | 612 |           PRINT(zeroes, 1); | 
 | 613 |           if (prec || flags & ALT) PRINT(decimal_point, 1); | 
 | 614 |           PAD(-expt, zeroes); | 
 | 615 |           /* already handled initial 0's */ | 
 | 616 |           prec += expt; | 
 | 617 |         } else { | 
 | 618 |           PRINTANDPAD(cp, convbuf + ndig, lead, zeroes); | 
 | 619 |           cp += lead; | 
 | 620 |           if (prec || flags & ALT) PRINT(decimal_point, 1); | 
 | 621 |         } | 
 | 622 |         PRINTANDPAD(cp, convbuf + ndig, prec, zeroes); | 
 | 623 |       } else { /* %[eE] or sufficiently long %[gG] */ | 
 | 624 |         if (prec > 1 || flags & ALT) { | 
 | 625 |           buf[0] = *cp++; | 
 | 626 |           buf[1] = *decimal_point; | 
 | 627 |           PRINT(buf, 2); | 
 | 628 |           PRINT(cp, ndig - 1); | 
 | 629 |           PAD(prec - ndig, zeroes); | 
 | 630 |         } else { /* XeYYY */ | 
 | 631 |           PRINT(cp, 1); | 
 | 632 |         } | 
 | 633 |         PRINT(expstr, expsize); | 
 | 634 |       } | 
 | 635 |     } | 
 | 636 |     /* left-adjusting padding (always blank) */ | 
 | 637 |     if (flags & LADJUST) PAD(width - realsz, blanks); | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 638 |  | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 639 |     /* finally, adjust ret */ | 
 | 640 |     if (width < realsz) width = realsz; | 
 | 641 |     if (width > INT_MAX - ret) goto overflow; | 
 | 642 |     ret += width; | 
 | 643 |   } | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 644 | done: | 
 | 645 | error: | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 646 |   va_end(orgap); | 
 | 647 |   if (__sferror(fp)) ret = -1; | 
 | 648 |   goto finish; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 649 |  | 
 | 650 | overflow: | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 651 |   errno = ENOMEM; | 
 | 652 |   ret = -1; | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 653 |  | 
 | 654 | finish: | 
| Elliott Hughes | c8f2c52 | 2017-10-31 13:07:51 -0700 | [diff] [blame] | 655 |   free(convbuf); | 
 | 656 |   if (dtoaresult) __freedtoa(dtoaresult); | 
 | 657 |   if (argtable != NULL && argtable != statargtable) { | 
 | 658 |     munmap(argtable, argtablesiz); | 
 | 659 |     argtable = NULL; | 
 | 660 |   } | 
 | 661 |   return (ret); | 
| Elliott Hughes | 94336d8 | 2014-04-29 17:39:29 -0700 | [diff] [blame] | 662 | } |