| /*	$OpenBSD: vfwprintf.c,v 1.15 2015/12/28 22:08:18 mmcc Exp $ */ | 
 | /*- | 
 |  * Copyright (c) 1990 The Regents of the University of California. | 
 |  * All rights reserved. | 
 |  * | 
 |  * This code is derived from software contributed to Berkeley by | 
 |  * Chris Torek. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * 3. Neither the name of the University nor the names of its contributors | 
 |  *    may be used to endorse or promote products derived from this software | 
 |  *    without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 |  * SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #define CHAR_TYPE wchar_t | 
 | #define FUNCTION_NAME __vfwprintf | 
 | #define CHAR_TYPE_STRLEN wcslen | 
 | #define CHAR_TYPE_STRNLEN wcsnlen | 
 | #define CHAR_TYPE_INF L"INF" | 
 | #define CHAR_TYPE_inf L"inf" | 
 | #define CHAR_TYPE_NAN L"NAN" | 
 | #define CHAR_TYPE_nan L"nan" | 
 | #define CHAR_TYPE_ORIENTATION ORIENT_CHARS | 
 |  | 
 | #define PRINT(ptr, len)                                          \ | 
 |   do {                                                           \ | 
 |     for (int n3 = 0; n3 < (len); n3++) {                         \ | 
 |       if ((helpers::xfputwc((ptr)[n3], fp)) == WEOF) goto error; \ | 
 |     }                                                            \ | 
 |   } while (0) | 
 |  | 
 | #define FLUSH() | 
 |  | 
 | #include "printf_common.h" | 
 |  | 
 | #define print_utf8(utf8, prec) \ | 
 |   do { \ | 
 |     free(convbuf); \ | 
 |     convbuf = helpers::mbsconv(utf8, prec); \ | 
 |     if (convbuf == nullptr) { \ | 
 |       fp->_flags |= __SERR; \ | 
 |       goto error; \ | 
 |     } else { \ | 
 |       cp = convbuf; \ | 
 |     } \ | 
 |     goto string; \ | 
 |   } while (0) | 
 |  | 
 | int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) { | 
 |   int caller_errno = errno; | 
 |   int n, n2; | 
 |   CHAR_TYPE* cp;   /* handy char pointer (short term usage) */ | 
 |   CHAR_TYPE sign;  /* sign prefix (' ', '+', '-', or \0) */ | 
 |   int flags;     /* flags as above */ | 
 |   int ret;       /* return value accumulator */ | 
 |   int width;     /* width from format (%8d), or 0 */ | 
 |   int prec;      /* precision from format; <0 for N/A */ | 
 |   /* | 
 |    * We can decompose the printed representation of floating | 
 |    * point numbers into several parts, some of which may be empty: | 
 |    * | 
 |    * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ | 
 |    *    A       B     ---C---      D       E   F | 
 |    * | 
 |    * A:	'sign' holds this value if present; '\0' otherwise | 
 |    * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal | 
 |    * C:	cp points to the string MMMNNN.  Leading and trailing | 
 |    *	zeros are not in the string and must be added. | 
 |    * D:	expchar holds this character; '\0' if no exponent, e.g. %f | 
 |    * F:	at least two digits for decimal, at least one digit for hex | 
 |    */ | 
 |   char* decimal_point = nullptr; | 
 |   int signflag; /* true if float is negative */ | 
 |   union {       /* floating point arguments %[aAeEfFgG] */ | 
 |     double dbl; | 
 |     long double ldbl; | 
 |   } fparg; | 
 |   int expt;                      /* integer value of exponent */ | 
 |   char expchar;                  /* exponent character: [eEpP\0] */ | 
 |   char* dtoaend;                 /* pointer to end of converted digits */ | 
 |   int expsize;                   /* character count for expstr */ | 
 |   int lead;                      /* sig figs before decimal or group sep */ | 
 |   int ndig;                      /* actual number of digits returned by dtoa */ | 
 |   CHAR_TYPE expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */ | 
 |   char* dtoaresult = nullptr; | 
 |  | 
 |   uintmax_t _umax;             /* integer arguments %[diouxX] */ | 
 |   enum { BIN, OCT, DEC, HEX } base; /* base for %[bBdiouxX] conversion */ | 
 |   int dprec;                   /* a copy of prec if %[bBdiouxX], 0 otherwise */ | 
 |   int realsz;                  /* field size expanded by dprec */ | 
 |   int size;                    /* size of converted field or string */ | 
 |   const char* xdigs;           /* digits for %[xX] conversion */ | 
 | #define NIOV 8 | 
 |   struct __suio uio;       /* output information: summary */ | 
 |   struct __siov iov[NIOV]; /* ... and individual io vectors */ | 
 |   struct __siov* iovp; /* for PRINT macro */ | 
 |   CHAR_TYPE buf[BUF];            /* buffer with space for digits of uintmax_t */ | 
 |   CHAR_TYPE ox[2];               /* space for 0x; ox[1] is either x, X, or \0 */ | 
 |   union arg* argtable;         /* args, built due to positional arg */ | 
 |   union arg statargtable[STATIC_ARG_TBL_SIZE]; | 
 |   size_t argtablesiz; | 
 |   int nextarg;      /* 1-based argument index */ | 
 |   va_list orgap;    /* original argument pointer */ | 
 |   CHAR_TYPE* convbuf; /* buffer for wide/multibyte conversion */ | 
 |  | 
 |   /* | 
 |    * Choose PADSIZE to trade efficiency vs. size.  If larger printf | 
 |    * fields occur frequently, increase PADSIZE and make the initialisers | 
 |    * below longer. | 
 |    */ | 
 | #define PADSIZE 16 /* pad chunk size */ | 
 |   static const CHAR_TYPE blanks[PADSIZE] = { | 
 |     ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' | 
 |   }; | 
 |   static const CHAR_TYPE zeroes[PADSIZE] = { | 
 |     '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' | 
 |   }; | 
 |  | 
 |   static const char xdigs_lower[] = "0123456789abcdef"; | 
 |   static const char xdigs_upper[] = "0123456789ABCDEF"; | 
 |  | 
 |   _SET_ORIENTATION(fp, CHAR_TYPE_ORIENTATION); | 
 |  | 
 |   // Writing "" to a read only file returns EOF, not 0. | 
 |   if (cantwrite(fp)) { | 
 |     errno = EBADF; | 
 |     return EOF; | 
 |   } | 
 |  | 
 |   // Optimize writes to stderr and other unbuffered files). | 
 |   if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0) { | 
 |     return (__sbprintf(fp, fmt0, ap)); | 
 |   } | 
 |  | 
 |   CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0); | 
 |   argtable = nullptr; | 
 |   nextarg = 1; | 
 |   va_copy(orgap, ap); | 
 |   uio.uio_iov = iovp = iov; | 
 |   uio.uio_resid = 0; | 
 |   uio.uio_iovcnt = 0; | 
 |   ret = 0; | 
 |   convbuf = nullptr; | 
 |  | 
 |   /* | 
 |    * Scan the format for conversions (`%' character). | 
 |    */ | 
 |   for (;;) { | 
 |     int ch; | 
 |     for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; | 
 |     if (fmt != cp) { | 
 |       ptrdiff_t m = fmt - cp; | 
 |       if (m < 0 || m > INT_MAX - ret) goto overflow; | 
 |       PRINT(cp, m); | 
 |       ret += m; | 
 |     } | 
 |     if (ch == '\0') goto done; | 
 |     fmt++; /* skip over '%' */ | 
 |  | 
 |     flags = 0; | 
 |     dprec = 0; | 
 |     width = 0; | 
 |     prec = -1; | 
 |     sign = '\0'; | 
 |     ox[1] = '\0'; | 
 |  | 
 |   rflag: | 
 |     ch = *fmt++; | 
 |   reswitch: | 
 |     switch (ch) { | 
 |       case ' ': | 
 |         /* | 
 |          * ``If the space and + flags both appear, the space | 
 |          * flag will be ignored.'' | 
 |          *	-- ANSI X3J11 | 
 |          */ | 
 |         if (!sign) sign = ' '; | 
 |         goto rflag; | 
 |       case '#': | 
 |         flags |= ALT; | 
 |         goto rflag; | 
 |       case '\'': | 
 |         /* grouping not implemented */ | 
 |         goto rflag; | 
 |       case '*': | 
 |         /* | 
 |          * ``A negative field width argument is taken as a | 
 |          * - flag followed by a positive field width.'' | 
 |          *	-- ANSI X3J11 | 
 |          * They don't exclude field widths read from args. | 
 |          */ | 
 |         GETASTER(width); | 
 |         if (width >= 0) goto rflag; | 
 |         if (width == INT_MIN) goto overflow; | 
 |         width = -width; | 
 |         __BIONIC_FALLTHROUGH; | 
 |       case '-': | 
 |         flags |= LADJUST; | 
 |         goto rflag; | 
 |       case '+': | 
 |         sign = '+'; | 
 |         goto rflag; | 
 |       case '.': | 
 |         if ((ch = *fmt++) == '*') { | 
 |           GETASTER(n); | 
 |           prec = n < 0 ? -1 : n; | 
 |           goto rflag; | 
 |         } | 
 |         n = 0; | 
 |         while (is_digit(ch)) { | 
 |           APPEND_DIGIT(n, ch); | 
 |           ch = *fmt++; | 
 |         } | 
 |         if (ch == '$') { | 
 |           nextarg = n; | 
 |           if (argtable == nullptr) { | 
 |             argtable = statargtable; | 
 |             if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { | 
 |               ret = -1; | 
 |               goto error; | 
 |             } | 
 |           } | 
 |           goto rflag; | 
 |         } | 
 |         prec = n; | 
 |         goto reswitch; | 
 |       case '0': | 
 |         /* | 
 |          * ``Note that 0 is taken as a flag, not as the | 
 |          * beginning of a field width.'' | 
 |          *	-- ANSI X3J11 | 
 |          */ | 
 |         flags |= ZEROPAD; | 
 |         goto rflag; | 
 |       case '1': | 
 |       case '2': | 
 |       case '3': | 
 |       case '4': | 
 |       case '5': | 
 |       case '6': | 
 |       case '7': | 
 |       case '8': | 
 |       case '9': | 
 |         n = 0; | 
 |         do { | 
 |           APPEND_DIGIT(n, ch); | 
 |           ch = *fmt++; | 
 |         } while (is_digit(ch)); | 
 |         if (ch == '$') { | 
 |           nextarg = n; | 
 |           if (argtable == nullptr) { | 
 |             argtable = statargtable; | 
 |             if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { | 
 |               ret = -1; | 
 |               goto error; | 
 |             } | 
 |           } | 
 |           goto rflag; | 
 |         } | 
 |         width = n; | 
 |         goto reswitch; | 
 |       case 'L': | 
 |         flags |= LONGDBL; | 
 |         goto rflag; | 
 |       case 'h': | 
 |         if (*fmt == 'h') { | 
 |           fmt++; | 
 |           flags |= CHARINT; | 
 |         } else { | 
 |           flags |= SHORTINT; | 
 |         } | 
 |         goto rflag; | 
 |       case 'j': | 
 |         flags |= MAXINT; | 
 |         goto rflag; | 
 |       case 'l': | 
 |         if (*fmt == 'l') { | 
 |           fmt++; | 
 |           flags |= LLONGINT; | 
 |         } else { | 
 |           flags |= LONGINT; | 
 |         } | 
 |         goto rflag; | 
 |       case 'q': | 
 |         flags |= LLONGINT; | 
 |         goto rflag; | 
 |       case 't': | 
 |         flags |= PTRINT; | 
 |         goto rflag; | 
 |       case 'z': | 
 |         flags |= SIZEINT; | 
 |         goto rflag; | 
 |       case 'B': | 
 |       case 'b': | 
 |         _umax = UARG(); | 
 |         base = BIN; | 
 |         if (flags & ALT && _umax != 0) ox[1] = ch; | 
 |         goto nosign; | 
 |       case 'C': | 
 |         flags |= LONGINT; | 
 |         __BIONIC_FALLTHROUGH; | 
 |       case 'c': | 
 |         if (flags & LONGINT) | 
 |           *(cp = buf) = (wchar_t)GETARG(wint_t); | 
 |         else | 
 |           *(cp = buf) = (wchar_t)btowc(GETARG(int)); | 
 |         size = 1; | 
 |         sign = '\0'; | 
 |         break; | 
 |       case 'D': | 
 |         flags |= LONGINT; | 
 |         __BIONIC_FALLTHROUGH; | 
 |       case 'd': | 
 |       case 'i': | 
 |         _umax = SARG(); | 
 | signed_decimal: | 
 |         if ((intmax_t)_umax < 0) { | 
 |           _umax = -_umax; | 
 |           sign = '-'; | 
 |         } | 
 |         base = DEC; | 
 |         goto number; | 
 |       case 'a': | 
 |       case 'A': | 
 |         if (ch == 'a') { | 
 |           ox[1] = 'x'; | 
 |           xdigs = xdigs_lower; | 
 |           expchar = 'p'; | 
 |         } else { | 
 |           ox[1] = 'X'; | 
 |           xdigs = xdigs_upper; | 
 |           expchar = 'P'; | 
 |         } | 
 |         if (prec >= 0) prec++; | 
 |         if (dtoaresult) __freedtoa(dtoaresult); | 
 |         if (flags & LONGDBL) { | 
 |           fparg.ldbl = GETARG(long double); | 
 |           dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend); | 
 |           if (dtoaresult == nullptr) { | 
 |             errno = ENOMEM; | 
 |             goto error; | 
 |           } | 
 |         } else { | 
 |           fparg.dbl = GETARG(double); | 
 |           dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend); | 
 |           if (dtoaresult == nullptr) { | 
 |             errno = ENOMEM; | 
 |             goto error; | 
 |           } | 
 |         } | 
 |         if (prec < 0) prec = dtoaend - dtoaresult; | 
 |         if (expt == INT_MAX) ox[1] = '\0'; | 
 |         goto fp_common; | 
 |       case 'e': | 
 |       case 'E': | 
 |         expchar = ch; | 
 |         if (prec < 0) /* account for digit before decpt */ | 
 |           prec = DEFPREC + 1; | 
 |         else | 
 |           prec++; | 
 |         goto fp_begin; | 
 |       case 'f': | 
 |       case 'F': | 
 |         expchar = '\0'; | 
 |         goto fp_begin; | 
 |       case 'g': | 
 |       case 'G': | 
 |         expchar = ch - ('g' - 'e'); | 
 |         if (prec == 0) prec = 1; | 
 |       fp_begin: | 
 |         if (prec < 0) prec = DEFPREC; | 
 |         if (dtoaresult) __freedtoa(dtoaresult); | 
 |         if (flags & LONGDBL) { | 
 |           fparg.ldbl = GETARG(long double); | 
 |           dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); | 
 |           if (dtoaresult == nullptr) { | 
 |             errno = ENOMEM; | 
 |             goto error; | 
 |           } | 
 |         } else { | 
 |           fparg.dbl = GETARG(double); | 
 |           dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); | 
 |           if (dtoaresult == nullptr) { | 
 |             errno = ENOMEM; | 
 |             goto error; | 
 |           } | 
 |           if (expt == 9999) expt = INT_MAX; | 
 |         } | 
 |       fp_common: | 
 | #if CHAR_TYPE_ORIENTATION == ORIENT_BYTES | 
 |         cp = dtoaresult; | 
 | #else | 
 |         free(convbuf); | 
 |         cp = convbuf = helpers::mbsconv(dtoaresult, -1); | 
 |         if (cp == nullptr) goto error; | 
 | #endif | 
 |         if (signflag) sign = '-'; | 
 |         if (expt == INT_MAX) { /* inf or nan */ | 
 |           if (*cp == 'N') { | 
 |             cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_nan : CHAR_TYPE_NAN); | 
 |           } else { | 
 |             cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_inf : CHAR_TYPE_INF); | 
 |           } | 
 |           size = 3; | 
 |           flags &= ~ZEROPAD; | 
 |           break; | 
 |         } | 
 |         flags |= FPT; | 
 |         ndig = dtoaend - dtoaresult; | 
 |         if (ch == 'g' || ch == 'G') { | 
 |           if (expt > -4 && expt <= prec) { | 
 |             /* Make %[gG] smell like %[fF] */ | 
 |             expchar = '\0'; | 
 |             if (flags & ALT) | 
 |               prec -= expt; | 
 |             else | 
 |               prec = ndig - expt; | 
 |             if (prec < 0) prec = 0; | 
 |           } else { | 
 |             /* | 
 |              * Make %[gG] smell like %[eE], but | 
 |              * trim trailing zeroes if no # flag. | 
 |              */ | 
 |             if (!(flags & ALT)) prec = ndig; | 
 |           } | 
 |         } | 
 |         if (expchar) { | 
 |           expsize = exponent(expstr, expt - 1, expchar); | 
 |           size = expsize + prec; | 
 |           if (prec > 1 || flags & ALT) ++size; | 
 |         } else { | 
 |           /* space for digits before decimal point */ | 
 |           if (expt > 0) | 
 |             size = expt; | 
 |           else /* "0" */ | 
 |             size = 1; | 
 |           /* space for decimal pt and following digits */ | 
 |           if (prec || flags & ALT) size += prec + 1; | 
 |           lead = expt; | 
 |         } | 
 |         break; | 
 |       case 'n': | 
 |         __fortify_fatal("%%n not allowed on Android"); | 
 |       case 'm': | 
 |         if (flags & ALT) { | 
 |           const char* name = strerrorname_np(caller_errno); | 
 |           if (name) print_utf8(name, prec); | 
 |           _umax = caller_errno; | 
 |           goto signed_decimal; | 
 |         } | 
 |         print_utf8(strerror_r(caller_errno, reinterpret_cast<char*>(buf), sizeof(buf)), prec); | 
 |       case 'O': | 
 |         flags |= LONGINT; | 
 |         __BIONIC_FALLTHROUGH; | 
 |       case 'o': | 
 |         _umax = UARG(); | 
 |         base = OCT; | 
 |         goto nosign; | 
 |       case 'p': | 
 |         /* | 
 |          * ``The argument shall be a pointer to void.  The | 
 |          * value of the pointer is converted to a sequence | 
 |          * of printable characters, in an implementation- | 
 |          * defined manner.'' | 
 |          *	-- ANSI X3J11 | 
 |          */ | 
 |         _umax = (u_long)GETARG(void*); | 
 |         base = HEX; | 
 |         xdigs = xdigs_lower; | 
 |         ox[1] = 'x'; | 
 |         goto nosign; | 
 |       case 'S': | 
 |         flags |= LONGINT; | 
 |         __BIONIC_FALLTHROUGH; | 
 |       case 's': | 
 |         if (flags & LONGINT) { | 
 |           if ((cp = GETARG(wchar_t*)) == nullptr) cp = const_cast<wchar_t*>(L"(null)"); | 
 |         } else { | 
 |           char* mbsarg; | 
 |           if ((mbsarg = GETARG(char*)) == nullptr) mbsarg = const_cast<char*>("(null)"); | 
 |           print_utf8(mbsarg, prec); | 
 |         } | 
 |   string: | 
 |         if (prec >= 0) { | 
 |           size = CHAR_TYPE_STRNLEN(cp, prec); | 
 |         } else { | 
 |           size_t len; | 
 |  | 
 |           if ((len = CHAR_TYPE_STRLEN(cp)) > INT_MAX) goto overflow; | 
 |           size = (int)len; | 
 |         } | 
 |         sign = '\0'; | 
 |         break; | 
 |       case 'U': | 
 |         flags |= LONGINT; | 
 |         __BIONIC_FALLTHROUGH; | 
 |       case 'u': | 
 |         _umax = UARG(); | 
 |         base = DEC; | 
 |         goto nosign; | 
 |       case 'w': { | 
 |         n = 0; | 
 |         bool fast = false; | 
 |         ch = *fmt++; | 
 |         if (ch == 'f') { | 
 |           fast = true; | 
 |           ch = *fmt++; | 
 |         } | 
 |         while (is_digit(ch)) { | 
 |           APPEND_DIGIT(n, ch); | 
 |           ch = *fmt++; | 
 |         } | 
 |         flags |= helpers::w_to_flag(n, fast); | 
 |         goto reswitch; | 
 |       } | 
 |       case 'X': | 
 |         xdigs = xdigs_upper; | 
 |         goto hex; | 
 |       case 'x': | 
 |         xdigs = xdigs_lower; | 
 |       hex: | 
 |         _umax = UARG(); | 
 |         base = HEX; | 
 |         /* leading 0x/X only if non-zero */ | 
 |         if (flags & ALT && _umax != 0) ox[1] = ch; | 
 |  | 
 |         /* unsigned conversions */ | 
 |       nosign: | 
 |         sign = '\0'; | 
 |         /* | 
 |          * ``... diouXx conversions ... if a precision is | 
 |          * specified, the 0 flag will be ignored.'' | 
 |          *	-- ANSI X3J11 | 
 |          */ | 
 |       number: | 
 |         if ((dprec = prec) >= 0) flags &= ~ZEROPAD; | 
 |  | 
 |         /* | 
 |          * ``The result of converting a zero value with an | 
 |          * explicit precision of zero is no characters.'' | 
 |          *	-- ANSI X3J11 | 
 |          */ | 
 |         cp = buf + BUF; | 
 |         if (_umax != 0 || prec != 0) { | 
 |           /* | 
 |            * Unsigned mod is hard, and unsigned mod | 
 |            * by a constant is easier than that by | 
 |            * a variable; hence this switch. | 
 |            */ | 
 |           switch (base) { | 
 |             case BIN: | 
 |               do { | 
 |                 *--cp = to_char(_umax & 1); | 
 |                 _umax >>= 1; | 
 |               } while (_umax); | 
 |               break; | 
 |  | 
 |             case OCT: | 
 |               do { | 
 |                 *--cp = to_char(_umax & 7); | 
 |                 _umax >>= 3; | 
 |               } while (_umax); | 
 |               /* handle octal leading 0 */ | 
 |               if (flags & ALT && *cp != '0') *--cp = '0'; | 
 |               break; | 
 |  | 
 |             case DEC: | 
 |               /* many numbers are 1 digit */ | 
 |               while (_umax >= 10) { | 
 |                 *--cp = to_char(_umax % 10); | 
 |                 _umax /= 10; | 
 |               } | 
 |               *--cp = to_char(_umax); | 
 |               break; | 
 |  | 
 |             case HEX: | 
 |               do { | 
 |                 *--cp = xdigs[_umax & 15]; | 
 |                 _umax >>= 4; | 
 |               } while (_umax); | 
 |               break; | 
 |  | 
 |             default: | 
 |               abort(); | 
 |           } | 
 |         } | 
 |         size = buf + BUF - cp; | 
 |         if (size > BUF) abort(); /* should never happen */ | 
 |         break; | 
 |       default: /* "%?" prints ?, unless ? is NUL */ | 
 |         if (ch == '\0') goto done; | 
 |         /* pretend it was %c with argument ch */ | 
 |         cp = buf; | 
 |         *cp = ch; | 
 |         size = 1; | 
 |         sign = '\0'; | 
 |         break; | 
 |     } | 
 |  | 
 |     /* | 
 |      * All reasonable formats wind up here.  At this point, `cp' | 
 |      * points to a string which (if not flags&LADJUST) should be | 
 |      * padded out to `width' places.  If flags&ZEROPAD, it should | 
 |      * first be prefixed by any sign or other prefix; otherwise, | 
 |      * it should be blank padded before the prefix is emitted. | 
 |      * After any left-hand padding and prefixing, emit zeroes | 
 |      * required by a decimal %[bBdiouxX] precision, then print the | 
 |      * string proper, then emit zeroes required by any leftover | 
 |      * floating precision; finally, if LADJUST, pad with blanks. | 
 |      * | 
 |      * Compute actual size, so we know how much to pad. | 
 |      * size excludes decimal prec; realsz includes it. | 
 |      */ | 
 |     realsz = dprec > size ? dprec : size; | 
 |     if (sign) realsz++; | 
 |     if (ox[1]) realsz += 2; | 
 |  | 
 |     /* right-adjusting blank padding */ | 
 |     if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks); | 
 |  | 
 |     /* prefix */ | 
 |     if (sign) PRINT(&sign, 1); | 
 |     if (ox[1]) { /* ox[1] is either x, X, or \0 */ | 
 |       ox[0] = '0'; | 
 |       PRINT(ox, 2); | 
 |     } | 
 |  | 
 |     /* right-adjusting zero padding */ | 
 |     if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes); | 
 |  | 
 |     /* leading zeroes from decimal precision */ | 
 |     PAD(dprec - size, zeroes); | 
 |  | 
 |     /* the string or number proper */ | 
 |     if ((flags & FPT) == 0) { | 
 |       PRINT(cp, size); | 
 |     } else { /* glue together f_p fragments */ | 
 |       if (decimal_point == nullptr) decimal_point = nl_langinfo(RADIXCHAR); | 
 |       if (!expchar) { /* %[fF] or sufficiently short %[gG] */ | 
 |         CHAR_TYPE* end = cp + ndig; | 
 |         if (expt <= 0) { | 
 |           PRINT(zeroes, 1); | 
 |           if (prec || flags & ALT) PRINT(decimal_point, 1); | 
 |           PAD(-expt, zeroes); | 
 |           /* already handled initial 0's */ | 
 |           prec += expt; | 
 |         } else { | 
 |           PRINTANDPAD(cp, end, lead, zeroes); | 
 |           cp += lead; | 
 |           if (prec || flags & ALT) PRINT(decimal_point, 1); | 
 |         } | 
 |         PRINTANDPAD(cp, end, prec, zeroes); | 
 |       } else { /* %[eE] or sufficiently long %[gG] */ | 
 |         if (prec > 1 || flags & ALT) { | 
 |           buf[0] = *cp++; | 
 |           buf[1] = *decimal_point; | 
 |           PRINT(buf, 2); | 
 |           PRINT(cp, ndig - 1); | 
 |           PAD(prec - ndig, zeroes); | 
 |         } else { /* XeYYY */ | 
 |           PRINT(cp, 1); | 
 |         } | 
 |         PRINT(expstr, expsize); | 
 |       } | 
 |     } | 
 |     /* left-adjusting padding (always blank) */ | 
 |     if (flags & LADJUST) PAD(width - realsz, blanks); | 
 |  | 
 |     /* finally, adjust ret */ | 
 |     if (width < realsz) width = realsz; | 
 |     if (width > INT_MAX - ret) goto overflow; | 
 |     ret += width; | 
 |  | 
 |     FLUSH(); /* copy out the I/O vectors */ | 
 |   } | 
 | done: | 
 |   FLUSH(); | 
 | error: | 
 |   va_end(orgap); | 
 |   if (__sferror(fp)) ret = -1; | 
 |   goto finish; | 
 |  | 
 | overflow: | 
 |   errno = ENOMEM; | 
 |   ret = -1; | 
 |  | 
 | finish: | 
 |   free(convbuf); | 
 |   if (dtoaresult) __freedtoa(dtoaresult); | 
 |   if (argtable != nullptr && argtable != statargtable) { | 
 |     munmap(argtable, argtablesiz); | 
 |     argtable = nullptr; | 
 |   } | 
 |   return (ret); | 
 | } |