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