]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: import FreeBSD's printf
authorDavid Lamparter <equinox@diac24.net>
Sun, 12 May 2019 18:03:09 +0000 (20:03 +0200)
committerDavid Lamparter <equinox@diac24.net>
Mon, 3 Jun 2019 14:43:11 +0000 (16:43 +0200)
... from current SVN HEAD (not that it has been touched in the past 2
years ...)

Signed-off-by: David Lamparter <equinox@diac24.net>
lib/printf/README [new file with mode: 0644]
lib/printf/printf-pos.c [new file with mode: 0644]
lib/printf/printfcommon.h [new file with mode: 0644]
lib/printf/printflocal.h [new file with mode: 0644]
lib/printf/vfprintf.c [new file with mode: 0644]

diff --git a/lib/printf/README b/lib/printf/README
new file mode 100644 (file)
index 0000000..ac28364
--- /dev/null
@@ -0,0 +1,9 @@
+This is the printf implementation from FreeBSD.  It was imported on 2019-05-12,
+from SVN revision 347514 (but the code hadn't been touched for 2 years before
+that.)
+
+Please don't reindent or otherwise mangle the files to make importing fixes
+easy (not that there are significant changes likely to happen...)
+
+The changes to this code are published under FreeBSD's license as listed in the
+file headers.  If you change license, please make that as obvious as possible.
diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c
new file mode 100644 (file)
index 0000000..40f91e8
--- /dev/null
@@ -0,0 +1,776 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990, 1993
+ *     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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * This is the code responsible for handling positional arguments
+ * (%m$ and %m$.n$) for vfprintf() and vfwprintf().
+ */
+
+#include "namespace.h"
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include "un-namespace.h"
+#include "printflocal.h"
+
+#ifdef NL_ARGMAX
+#define        MAX_POSARG      NL_ARGMAX
+#else
+#define        MAX_POSARG      65536
+#endif
+
+/*
+ * Type ids for argument type table.
+ */
+enum typeid {
+       T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT,
+       T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG,
+       T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SSIZET,
+       T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR,
+       T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR
+};
+
+/* An expandable array of types. */
+struct typetable {
+       enum typeid *table; /* table of types */
+       enum typeid stattable[STATIC_ARG_TBL_SIZE];
+       u_int tablesize;        /* current size of type table */
+       u_int tablemax;         /* largest used index in table */
+       u_int nextarg;          /* 1-based argument index */
+};
+
+static int     __grow_type_table(struct typetable *);
+static void    build_arg_table (struct typetable *, va_list, union arg **);
+
+/*
+ * Initialize a struct typetable.
+ */
+static inline void
+inittypes(struct typetable *types)
+{
+       u_int n;
+
+       types->table = types->stattable;
+       types->tablesize = STATIC_ARG_TBL_SIZE;
+       types->tablemax = 0; 
+       types->nextarg = 1;
+       for (n = 0; n < STATIC_ARG_TBL_SIZE; n++)
+               types->table[n] = T_UNUSED;
+}
+
+/*
+ * struct typetable destructor.
+ */ 
+static inline void
+freetypes(struct typetable *types)
+{
+
+       if (types->table != types->stattable)
+               free (types->table);
+}
+
+/*
+ * Ensure that there is space to add a new argument type to the type table.
+ * Expand the table if necessary. Returns 0 on success.
+ */
+static inline int
+_ensurespace(struct typetable *types)
+{
+
+       if (types->nextarg >= types->tablesize) {
+               if (__grow_type_table(types))
+                       return (-1);
+       }
+       if (types->nextarg > types->tablemax)
+               types->tablemax = types->nextarg;
+       return (0);
+}
+
+/*
+ * Add an argument type to the table, expanding if necessary.
+ * Returns 0 on success.
+ */
+static inline int
+addtype(struct typetable *types, enum typeid type)
+{
+
+       if (_ensurespace(types))
+               return (-1);
+       types->table[types->nextarg++] = type;
+       return (0);
+}
+
+static inline int
+addsarg(struct typetable *types, int flags)
+{
+
+       if (_ensurespace(types))
+               return (-1);
+       if (flags & INTMAXT)
+               types->table[types->nextarg++] = T_INTMAXT;
+       else if (flags & SIZET)
+               types->table[types->nextarg++] = T_SSIZET;
+       else if (flags & PTRDIFFT)
+               types->table[types->nextarg++] = T_PTRDIFFT;
+       else if (flags & LLONGINT)
+               types->table[types->nextarg++] = T_LLONG;
+       else if (flags & LONGINT)
+               types->table[types->nextarg++] = T_LONG;
+       else
+               types->table[types->nextarg++] = T_INT;
+       return (0);
+}
+
+static inline int
+adduarg(struct typetable *types, int flags)
+{
+
+       if (_ensurespace(types))
+               return (-1);
+       if (flags & INTMAXT)
+               types->table[types->nextarg++] = T_UINTMAXT;
+       else if (flags & SIZET)
+               types->table[types->nextarg++] = T_SIZET;
+       else if (flags & PTRDIFFT)
+               types->table[types->nextarg++] = T_SIZET;
+       else if (flags & LLONGINT)
+               types->table[types->nextarg++] = T_U_LLONG;
+       else if (flags & LONGINT)
+               types->table[types->nextarg++] = T_U_LONG;
+       else
+               types->table[types->nextarg++] = T_U_INT;
+       return (0);
+}
+
+/*
+ * Add * arguments to the type array.
+ */
+static inline int
+addaster(struct typetable *types, char **fmtp)
+{
+       char *cp;
+       u_int n2;
+
+       n2 = 0;
+       cp = *fmtp;
+       while (is_digit(*cp)) {
+               n2 = 10 * n2 + to_digit(*cp);
+               cp++;
+       }
+       if (*cp == '$') {
+               u_int hold = types->nextarg;
+               types->nextarg = n2;
+               if (addtype(types, T_INT))
+                       return (-1);
+               types->nextarg = hold;
+               *fmtp = ++cp;
+       } else {
+               if (addtype(types, T_INT))
+                       return (-1);
+       }
+       return (0);
+}
+
+static inline int
+addwaster(struct typetable *types, wchar_t **fmtp)
+{
+       wchar_t *cp;
+       u_int n2;
+
+       n2 = 0;
+       cp = *fmtp;
+       while (is_digit(*cp)) {
+               n2 = 10 * n2 + to_digit(*cp);
+               cp++;
+       }
+       if (*cp == '$') {
+               u_int hold = types->nextarg;
+               types->nextarg = n2;
+               if (addtype(types, T_INT))
+                       return (-1);
+               types->nextarg = hold;
+               *fmtp = ++cp;
+       } else {
+               if (addtype(types, T_INT))
+                       return (-1);
+       }
+       return (0);
+}
+
+/*
+ * Find all arguments when a positional parameter is encountered.  Returns a
+ * table, indexed by argument number, of pointers to each arguments.  The
+ * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
+ * It will be replaces with a malloc-ed one if it overflows.
+ * Returns 0 on success. On failure, returns nonzero and sets errno.
+ */ 
+int
+__find_arguments (const char *fmt0, va_list ap, union arg **argtable)
+{
+       char *fmt;              /* format string */
+       int ch;                 /* character from fmt */
+       u_int n;                /* handy integer (short term usage) */
+       int error;
+       int flags;              /* flags as above */
+       struct typetable types; /* table of types */
+
+       fmt = (char *)fmt0;
+       inittypes(&types);
+       error = 0;
+
+       /*
+        * Scan the format for conversions (`%' character).
+        */
+       for (;;) {
+               while ((ch = *fmt) != '\0' && ch != '%')
+                       fmt++;
+               if (ch == '\0')
+                       goto done;
+               fmt++;          /* skip over '%' */
+
+               flags = 0;
+
+rflag:         ch = *fmt++;
+reswitch:      switch (ch) {
+               case ' ':
+               case '#':
+                       goto rflag;
+               case '*':
+                       if ((error = addaster(&types, &fmt)))
+                               goto error;
+                       goto rflag;
+               case '-':
+               case '+':
+               case '\'':
+                       goto rflag;
+               case '.':
+                       if ((ch = *fmt++) == '*') {
+                               if ((error = addaster(&types, &fmt)))
+                                       goto error;
+                               goto rflag;
+                       }
+                       while (is_digit(ch)) {
+                               ch = *fmt++;
+                       }
+                       goto reswitch;
+               case '0':
+                       goto rflag;
+               case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       n = 0;
+                       do {
+                               n = 10 * n + to_digit(ch);
+                               /* Detect overflow */
+                               if (n > MAX_POSARG) {
+                                       error = -1;
+                                       goto error;
+                               }
+                               ch = *fmt++;
+                       } while (is_digit(ch));
+                       if (ch == '$') {
+                               types.nextarg = n;
+                               goto rflag;
+                       }
+                       goto reswitch;
+#ifndef NO_FLOATING_POINT
+               case 'L':
+                       flags |= LONGDBL;
+                       goto rflag;
+#endif
+               case 'h':
+                       if (flags & SHORTINT) {
+                               flags &= ~SHORTINT;
+                               flags |= CHARINT;
+                       } else
+                               flags |= SHORTINT;
+                       goto rflag;
+               case 'j':
+                       flags |= INTMAXT;
+                       goto rflag;
+               case 'l':
+                       if (flags & LONGINT) {
+                               flags &= ~LONGINT;
+                               flags |= LLONGINT;
+                       } else
+                               flags |= LONGINT;
+                       goto rflag;
+               case 'q':
+                       flags |= LLONGINT;      /* not necessarily */
+                       goto rflag;
+               case 't':
+                       flags |= PTRDIFFT;
+                       goto rflag;
+               case 'z':
+                       flags |= SIZET;
+                       goto rflag;
+               case 'C':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'c':
+                       error = addtype(&types,
+                                       (flags & LONGINT) ? T_WINT : T_INT);
+                       if (error)
+                               goto error;
+                       break;
+               case 'D':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'd':
+               case 'i':
+                       if ((error = addsarg(&types, flags)))
+                               goto error;
+                       break;
+#ifndef NO_FLOATING_POINT
+               case 'a':
+               case 'A':
+               case 'e':
+               case 'E':
+               case 'f':
+               case 'g':
+               case 'G':
+                       error = addtype(&types,
+                           (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE);
+                       if (error)
+                               goto error;
+                       break;
+#endif /* !NO_FLOATING_POINT */
+               case 'n':
+                       if (flags & INTMAXT)
+                               error = addtype(&types, TP_INTMAXT);
+                       else if (flags & PTRDIFFT)
+                               error = addtype(&types, TP_PTRDIFFT);
+                       else if (flags & SIZET)
+                               error = addtype(&types, TP_SSIZET);
+                       else if (flags & LLONGINT)
+                               error = addtype(&types, TP_LLONG);
+                       else if (flags & LONGINT)
+                               error = addtype(&types, TP_LONG);
+                       else if (flags & SHORTINT)
+                               error = addtype(&types, TP_SHORT);
+                       else if (flags & CHARINT)
+                               error = addtype(&types, TP_SCHAR);
+                       else
+                               error = addtype(&types, TP_INT);
+                       if (error)
+                               goto error;
+                       continue;       /* no output */
+               case 'O':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'o':
+                       if ((error = adduarg(&types, flags)))
+                               goto error;
+                       break;
+               case 'p':
+                       if ((error = addtype(&types, TP_VOID)))
+                               goto error;
+                       break;
+               case 'S':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 's':
+                       error = addtype(&types,
+                                       (flags & LONGINT) ? TP_WCHAR : TP_CHAR);
+                       if (error)
+                               goto error;
+                       break;
+               case 'U':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'u':
+               case 'X':
+               case 'x':
+                       if ((error = adduarg(&types, flags)))
+                               goto error;
+                       break;
+               default:        /* "%?" prints ?, unless ? is NUL */
+                       if (ch == '\0')
+                               goto done;
+                       break;
+               }
+       }
+done:
+       build_arg_table(&types, ap, argtable);
+error:
+       freetypes(&types);
+       return (error || *argtable == NULL);
+}
+
+/* wchar version of __find_arguments. */
+int
+__find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable)
+{
+       wchar_t *fmt;           /* format string */
+       wchar_t ch;             /* character from fmt */
+       u_int n;                /* handy integer (short term usage) */
+       int error;
+       int flags;              /* flags as above */
+       struct typetable types; /* table of types */
+
+       fmt = (wchar_t *)fmt0;
+       inittypes(&types);
+       error = 0;
+
+       /*
+        * Scan the format for conversions (`%' character).
+        */
+       for (;;) {
+               while ((ch = *fmt) != '\0' && ch != '%')
+                       fmt++;
+               if (ch == '\0')
+                       goto done;
+               fmt++;          /* skip over '%' */
+
+               flags = 0;
+
+rflag:         ch = *fmt++;
+reswitch:      switch (ch) {
+               case ' ':
+               case '#':
+                       goto rflag;
+               case '*':
+                       if ((error = addwaster(&types, &fmt)))
+                               goto error;
+                       goto rflag;
+               case '-':
+               case '+':
+               case '\'':
+                       goto rflag;
+               case '.':
+                       if ((ch = *fmt++) == '*') {
+                               if ((error = addwaster(&types, &fmt)))
+                                       goto error;
+                               goto rflag;
+                       }
+                       while (is_digit(ch)) {
+                               ch = *fmt++;
+                       }
+                       goto reswitch;
+               case '0':
+                       goto rflag;
+               case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       n = 0;
+                       do {
+                               n = 10 * n + to_digit(ch);
+                               /* Detect overflow */
+                               if (n > MAX_POSARG) {
+                                       error = -1;
+                                       goto error;
+                               }
+                               ch = *fmt++;
+                       } while (is_digit(ch));
+                       if (ch == '$') {
+                               types.nextarg = n;
+                               goto rflag;
+                       }
+                       goto reswitch;
+#ifndef NO_FLOATING_POINT
+               case 'L':
+                       flags |= LONGDBL;
+                       goto rflag;
+#endif
+               case 'h':
+                       if (flags & SHORTINT) {
+                               flags &= ~SHORTINT;
+                               flags |= CHARINT;
+                       } else
+                               flags |= SHORTINT;
+                       goto rflag;
+               case 'j':
+                       flags |= INTMAXT;
+                       goto rflag;
+               case 'l':
+                       if (flags & LONGINT) {
+                               flags &= ~LONGINT;
+                               flags |= LLONGINT;
+                       } else
+                               flags |= LONGINT;
+                       goto rflag;
+               case 'q':
+                       flags |= LLONGINT;      /* not necessarily */
+                       goto rflag;
+               case 't':
+                       flags |= PTRDIFFT;
+                       goto rflag;
+               case 'z':
+                       flags |= SIZET;
+                       goto rflag;
+               case 'C':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'c':
+                       error = addtype(&types,
+                                       (flags & LONGINT) ? T_WINT : T_INT);
+                       if (error)
+                               goto error;
+                       break;
+               case 'D':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'd':
+               case 'i':
+                       if ((error = addsarg(&types, flags)))
+                               goto error;
+                       break;
+#ifndef NO_FLOATING_POINT
+               case 'a':
+               case 'A':
+               case 'e':
+               case 'E':
+               case 'f':
+               case 'g':
+               case 'G':
+                       error = addtype(&types,
+                           (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE);
+                       if (error)
+                               goto error;
+                       break;
+#endif /* !NO_FLOATING_POINT */
+               case 'n':
+                       if (flags & INTMAXT)
+                               error = addtype(&types, TP_INTMAXT);
+                       else if (flags & PTRDIFFT)
+                               error = addtype(&types, TP_PTRDIFFT);
+                       else if (flags & SIZET)
+                               error = addtype(&types, TP_SSIZET);
+                       else if (flags & LLONGINT)
+                               error = addtype(&types, TP_LLONG);
+                       else if (flags & LONGINT)
+                               error = addtype(&types, TP_LONG);
+                       else if (flags & SHORTINT)
+                               error = addtype(&types, TP_SHORT);
+                       else if (flags & CHARINT)
+                               error = addtype(&types, TP_SCHAR);
+                       else
+                               error = addtype(&types, TP_INT);
+                       if (error)
+                               goto error;
+                       continue;       /* no output */
+               case 'O':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'o':
+                       if ((error = adduarg(&types, flags)))
+                               goto error;
+                       break;
+               case 'p':
+                       if ((error = addtype(&types, TP_VOID)))
+                               goto error;
+                       break;
+               case 'S':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 's':
+                       error = addtype(&types,
+                           (flags & LONGINT) ? TP_WCHAR : TP_CHAR);
+                       if (error)
+                               goto error;
+                       break;
+               case 'U':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'u':
+               case 'X':
+               case 'x':
+                       if ((error = adduarg(&types, flags)))
+                               goto error;
+                       break;
+               default:        /* "%?" prints ?, unless ? is NUL */
+                       if (ch == '\0')
+                               goto done;
+                       break;
+               }
+       }
+done:
+       build_arg_table(&types, ap, argtable);
+error:
+       freetypes(&types);
+       return (error || *argtable == NULL);
+}
+
+/*
+ * Increase the size of the type table. Returns 0 on success.
+ */
+static int
+__grow_type_table(struct typetable *types)
+{
+       enum typeid *const oldtable = types->table;
+       const int oldsize = types->tablesize;
+       enum typeid *newtable;
+       u_int n, newsize;
+
+       /* Detect overflow */
+       if (types->nextarg > NL_ARGMAX)
+               return (-1);
+
+       newsize = oldsize * 2;
+       if (newsize < types->nextarg + 1)
+               newsize = types->nextarg + 1;
+       if (oldsize == STATIC_ARG_TBL_SIZE) {
+               if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL)
+                       return (-1);
+               bcopy(oldtable, newtable, oldsize * sizeof(enum typeid));
+       } else {
+               newtable = reallocarray(oldtable, newsize, sizeof(enum typeid));
+               if (newtable == NULL)
+                       return (-1);
+       }
+       for (n = oldsize; n < newsize; n++)
+               newtable[n] = T_UNUSED;
+
+       types->table = newtable;
+       types->tablesize = newsize;
+
+       return (0);
+}
+
+/*
+ * Build the argument table from the completed type table.
+ * On malloc failure, *argtable is set to NULL.
+ */
+static void
+build_arg_table(struct typetable *types, va_list ap, union arg **argtable)
+{
+       u_int n;
+
+       if (types->tablemax >= STATIC_ARG_TBL_SIZE) {
+               *argtable = (union arg *)
+                   malloc (sizeof (union arg) * (types->tablemax + 1));
+               if (*argtable == NULL)
+                       return;
+       }
+
+       (*argtable) [0].intarg = 0;
+       for (n = 1; n <= types->tablemax; n++) {
+               switch (types->table[n]) {
+                   case T_UNUSED: /* whoops! */
+                       (*argtable) [n].intarg = va_arg (ap, int);
+                       break;
+                   case TP_SCHAR:
+                       (*argtable) [n].pschararg = va_arg (ap, signed char *);
+                       break;
+                   case TP_SHORT:
+                       (*argtable) [n].pshortarg = va_arg (ap, short *);
+                       break;
+                   case T_INT:
+                       (*argtable) [n].intarg = va_arg (ap, int);
+                       break;
+                   case T_U_INT:
+                       (*argtable) [n].uintarg = va_arg (ap, unsigned int);
+                       break;
+                   case TP_INT:
+                       (*argtable) [n].pintarg = va_arg (ap, int *);
+                       break;
+                   case T_LONG:
+                       (*argtable) [n].longarg = va_arg (ap, long);
+                       break;
+                   case T_U_LONG:
+                       (*argtable) [n].ulongarg = va_arg (ap, unsigned long);
+                       break;
+                   case TP_LONG:
+                       (*argtable) [n].plongarg = va_arg (ap, long *);
+                       break;
+                   case T_LLONG:
+                       (*argtable) [n].longlongarg = va_arg (ap, long long);
+                       break;
+                   case T_U_LLONG:
+                       (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long);
+                       break;
+                   case TP_LLONG:
+                       (*argtable) [n].plonglongarg = va_arg (ap, long long *);
+                       break;
+                   case T_PTRDIFFT:
+                       (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t);
+                       break;
+                   case TP_PTRDIFFT:
+                       (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *);
+                       break;
+                   case T_SIZET:
+                       (*argtable) [n].sizearg = va_arg (ap, size_t);
+                       break;
+                   case T_SSIZET:
+                       (*argtable) [n].sizearg = va_arg (ap, ssize_t);
+                       break;
+                   case TP_SSIZET:
+                       (*argtable) [n].pssizearg = va_arg (ap, ssize_t *);
+                       break;
+                   case T_INTMAXT:
+                       (*argtable) [n].intmaxarg = va_arg (ap, intmax_t);
+                       break;
+                   case T_UINTMAXT:
+                       (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t);
+                       break;
+                   case TP_INTMAXT:
+                       (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *);
+                       break;
+                   case T_DOUBLE:
+#ifndef NO_FLOATING_POINT
+                       (*argtable) [n].doublearg = va_arg (ap, double);
+#endif
+                       break;
+                   case T_LONG_DOUBLE:
+#ifndef NO_FLOATING_POINT
+                       (*argtable) [n].longdoublearg = va_arg (ap, long double);
+#endif
+                       break;
+                   case TP_CHAR:
+                       (*argtable) [n].pchararg = va_arg (ap, char *);
+                       break;
+                   case TP_VOID:
+                       (*argtable) [n].pvoidarg = va_arg (ap, void *);
+                       break;
+                   case T_WINT:
+                       (*argtable) [n].wintarg = va_arg (ap, wint_t);
+                       break;
+                   case TP_WCHAR:
+                       (*argtable) [n].pwchararg = va_arg (ap, wchar_t *);
+                       break;
+               }
+       }
+}
diff --git a/lib/printf/printfcommon.h b/lib/printf/printfcommon.h
new file mode 100644 (file)
index 0000000..c120f1e
--- /dev/null
@@ -0,0 +1,309 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ * Portions of this software were developed by David Chisnall
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This file defines common routines used by both printf and wprintf.
+ * You must define CHAR to either char or wchar_t prior to including this.
+ */
+
+
+#ifndef NO_FLOATING_POINT
+
+#define        dtoa            __dtoa
+#define        freedtoa        __freedtoa
+
+#include <float.h>
+#include <math.h>
+#include "floatio.h"
+#include "gdtoa.h"
+
+#define        DEFPREC         6
+
+static int exponent(CHAR *, int, CHAR);
+
+#endif /* !NO_FLOATING_POINT */
+
+static CHAR    *__ujtoa(uintmax_t, CHAR *, int, int, const char *);
+static CHAR    *__ultoa(u_long, CHAR *, int, int, const char *);
+
+#define NIOV 8
+struct io_state {
+       FILE *fp;
+       struct __suio uio;      /* output information: summary */
+       struct __siov iov[NIOV];/* ... and individual io vectors */
+};
+
+static inline void
+io_init(struct io_state *iop, FILE *fp)
+{
+
+       iop->uio.uio_iov = iop->iov;
+       iop->uio.uio_resid = 0;
+       iop->uio.uio_iovcnt = 0;
+       iop->fp = fp;
+}
+
+/*
+ * WARNING: The buffer passed to io_print() is not copied immediately; it must
+ * remain valid until io_flush() is called.
+ */
+static inline int
+io_print(struct io_state *iop, const CHAR * __restrict ptr, int len, locale_t locale)
+{
+
+       iop->iov[iop->uio.uio_iovcnt].iov_base = (char *)ptr;
+       iop->iov[iop->uio.uio_iovcnt].iov_len = len;
+       iop->uio.uio_resid += len;
+       if (++iop->uio.uio_iovcnt >= NIOV)
+               return (__sprint(iop->fp, &iop->uio, locale));
+       else
+               return (0);
+}
+
+/*
+ * 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 blanks[PADSIZE] =
+{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
+static const CHAR zeroes[PADSIZE] =
+{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
+
+/*
+ * Pad with blanks or zeroes. 'with' should point to either the blanks array
+ * or the zeroes array.
+ */
+static inline int
+io_pad(struct io_state *iop, int howmany, const CHAR * __restrict with,
+               locale_t locale)
+{
+       int n;
+
+       while (howmany > 0) {
+               n = (howmany >= PADSIZE) ? PADSIZE : howmany;
+               if (io_print(iop, with, n, locale))
+                       return (-1);
+               howmany -= n;
+       }
+       return (0);
+}
+
+/*
+ * Print exactly len characters of the string spanning p to ep, truncating
+ * or padding with 'with' as necessary.
+ */
+static inline int
+io_printandpad(struct io_state *iop, const CHAR *p, const CHAR *ep,
+              int len, const CHAR * __restrict with, locale_t locale)
+{
+       int p_len;
+
+       p_len = ep - p;
+       if (p_len > len)
+               p_len = len;
+       if (p_len > 0) {
+               if (io_print(iop, p, p_len, locale))
+                       return (-1);
+       } else {
+               p_len = 0;
+       }
+       return (io_pad(iop, len - p_len, with, locale));
+}
+
+static inline int
+io_flush(struct io_state *iop, locale_t locale)
+{
+
+       return (__sprint(iop->fp, &iop->uio, locale));
+}
+
+/*
+ * Convert an unsigned long to ASCII for printf purposes, returning
+ * a pointer to the first character of the string representation.
+ * Octal numbers can be forced to have a leading zero; hex numbers
+ * use the given digits.
+ */
+static CHAR *
+__ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs)
+{
+       CHAR *cp = endp;
+       long sval;
+
+       /*
+        * Handle the three cases separately, in the hope of getting
+        * better/faster code.
+        */
+       switch (base) {
+       case 10:
+               if (val < 10) { /* many numbers are 1 digit */
+                       *--cp = to_char(val);
+                       return (cp);
+               }
+               /*
+                * On many machines, unsigned arithmetic is harder than
+                * signed arithmetic, so we do at most one unsigned mod and
+                * divide; this is sufficient to reduce the range of
+                * the incoming value to where signed arithmetic works.
+                */
+               if (val > LONG_MAX) {
+                       *--cp = to_char(val % 10);
+                       sval = val / 10;
+               } else
+                       sval = val;
+               do {
+                       *--cp = to_char(sval % 10);
+                       sval /= 10;
+               } while (sval != 0);
+               break;
+
+       case 8:
+               do {
+                       *--cp = to_char(val & 7);
+                       val >>= 3;
+               } while (val);
+               if (octzero && *cp != '0')
+                       *--cp = '0';
+               break;
+
+       case 16:
+               do {
+                       *--cp = xdigs[val & 15];
+                       val >>= 4;
+               } while (val);
+               break;
+
+       default:                        /* oops */
+               abort();
+       }
+       return (cp);
+}
+
+/* Identical to __ultoa, but for intmax_t. */
+static CHAR *
+__ujtoa(uintmax_t val, CHAR *endp, int base, int octzero, const char *xdigs)
+{
+       CHAR *cp = endp;
+       intmax_t sval;
+
+       /* quick test for small values; __ultoa is typically much faster */
+       /* (perhaps instead we should run until small, then call __ultoa?) */
+       if (val <= ULONG_MAX)
+               return (__ultoa((u_long)val, endp, base, octzero, xdigs));
+       switch (base) {
+       case 10:
+               if (val < 10) {
+                       *--cp = to_char(val % 10);
+                       return (cp);
+               }
+               if (val > INTMAX_MAX) {
+                       *--cp = to_char(val % 10);
+                       sval = val / 10;
+               } else
+                       sval = val;
+               do {
+                       *--cp = to_char(sval % 10);
+                       sval /= 10;
+               } while (sval != 0);
+               break;
+
+       case 8:
+               do {
+                       *--cp = to_char(val & 7);
+                       val >>= 3;
+               } while (val);
+               if (octzero && *cp != '0')
+                       *--cp = '0';
+               break;
+
+       case 16:
+               do {
+                       *--cp = xdigs[val & 15];
+                       val >>= 4;
+               } while (val);
+               break;
+
+       default:
+               abort();
+       }
+       return (cp);
+}
+
+#ifndef NO_FLOATING_POINT
+
+static int
+exponent(CHAR *p0, int exp, CHAR fmtch)
+{
+       CHAR *p, *t;
+       CHAR expbuf[MAXEXPDIG];
+
+       p = p0;
+       *p++ = fmtch;
+       if (exp < 0) {
+               exp = -exp;
+               *p++ = '-';
+       }
+       else
+               *p++ = '+';
+       t = expbuf + MAXEXPDIG;
+       if (exp > 9) {
+               do {
+                       *--t = to_char(exp % 10);
+               } while ((exp /= 10) > 9);
+               *--t = to_char(exp);
+               for (; t < expbuf + MAXEXPDIG; *p++ = *t++);
+       }
+       else {
+               /*
+                * Exponents for decimal floating point conversions
+                * (%[eEgG]) must be at least two characters long,
+                * whereas exponents for hexadecimal conversions can
+                * be only one character long.
+                */
+               if (fmtch == 'e' || fmtch == 'E')
+                       *p++ = '0';
+               *p++ = to_char(exp);
+       }
+       return (p - p0);
+}
+
+#endif /* !NO_FLOATING_POINT */
diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h
new file mode 100644 (file)
index 0000000..70131d1
--- /dev/null
@@ -0,0 +1,96 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990, 1993
+ *     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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Flags used during conversion.
+ */
+#define        ALT             0x001           /* alternate form */
+#define        LADJUST         0x004           /* left adjustment */
+#define        LONGDBL         0x008           /* long double */
+#define        LONGINT         0x010           /* long integer */
+#define        LLONGINT        0x020           /* long long integer */
+#define        SHORTINT        0x040           /* short integer */
+#define        ZEROPAD         0x080           /* zero (as opposed to blank) pad */
+#define        FPT             0x100           /* Floating point number */
+#define        GROUPING        0x200           /* use grouping ("'" flag) */
+                                       /* C99 additional size modifiers: */
+#define        SIZET           0x400           /* size_t */
+#define        PTRDIFFT        0x800           /* ptrdiff_t */
+#define        INTMAXT         0x1000          /* intmax_t */
+#define        CHARINT         0x2000          /* print char using int format */
+
+/*
+ * Macros for converting digits to letters and vice versa
+ */
+#define        to_digit(c)     ((c) - '0')
+#define is_digit(c)    ((unsigned)to_digit(c) <= 9)
+#define        to_char(n)      ((n) + '0')
+
+/* Size of the static argument table. */
+#define STATIC_ARG_TBL_SIZE 8
+
+union arg {
+       int     intarg;
+       u_int   uintarg;
+       long    longarg;
+       u_long  ulongarg;
+       long long longlongarg;
+       unsigned long long ulonglongarg;
+       ptrdiff_t ptrdiffarg;
+       size_t  sizearg;
+       intmax_t intmaxarg;
+       uintmax_t uintmaxarg;
+       void    *pvoidarg;
+       char    *pchararg;
+       signed char *pschararg;
+       short   *pshortarg;
+       int     *pintarg;
+       long    *plongarg;
+       long long *plonglongarg;
+       ptrdiff_t *pptrdiffarg;
+       ssize_t *pssizearg;
+       intmax_t *pintmaxarg;
+#ifndef NO_FLOATING_POINT
+       double  doublearg;
+       long double longdoublearg;
+#endif
+       wint_t  wintarg;
+       wchar_t *pwchararg;
+};
+
+/* Handle positional parameters. */
+int    __find_arguments(const char *, va_list, union arg **);
+int    __find_warguments(const wchar_t *, va_list, union arg **);
diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c
new file mode 100644 (file)
index 0000000..70f5074
--- /dev/null
@@ -0,0 +1,1054 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ * Portions of this software were developed by David Chisnall
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Actual printf innards.
+ *
+ * This code is large and complicated...
+ */
+
+#include "namespace.h"
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <printf.h>
+
+#include <stdarg.h>
+#include "xlocale_private.h"
+#include "un-namespace.h"
+
+#include "libc_private.h"
+#include "local.h"
+#include "fvwrite.h"
+#include "printflocal.h"
+
+static int     __sprint(FILE *, struct __suio *, locale_t);
+static int     __sbprintf(FILE *, locale_t, const char *, va_list) __printflike(3, 0)
+       __noinline;
+static char    *__wcsconv(wchar_t *, int);
+
+#define        CHAR    char
+#include "printfcommon.h"
+
+struct grouping_state {
+       char *thousands_sep;    /* locale-specific thousands separator */
+       int thousep_len;        /* length of thousands_sep */
+       const char *grouping;   /* locale-specific numeric grouping rules */
+       int lead;               /* sig figs before decimal or group sep */
+       int nseps;              /* number of group separators with ' */
+       int nrepeats;           /* number of repeats of the last group */
+};
+
+/*
+ * Initialize the thousands' grouping state in preparation to print a
+ * number with ndigits digits. This routine returns the total number
+ * of bytes that will be needed.
+ */
+static int
+grouping_init(struct grouping_state *gs, int ndigits, locale_t loc)
+{
+       struct lconv *locale;
+
+       locale = localeconv_l(loc);
+       gs->grouping = locale->grouping;
+       gs->thousands_sep = locale->thousands_sep;
+       gs->thousep_len = strlen(gs->thousands_sep);
+
+       gs->nseps = gs->nrepeats = 0;
+       gs->lead = ndigits;
+       while (*gs->grouping != CHAR_MAX) {
+               if (gs->lead <= *gs->grouping)
+                       break;
+               gs->lead -= *gs->grouping;
+               if (*(gs->grouping+1)) {
+                       gs->nseps++;
+                       gs->grouping++;
+               } else
+                       gs->nrepeats++;
+       }
+       return ((gs->nseps + gs->nrepeats) * gs->thousep_len);
+}
+
+/*
+ * Print a number with thousands' separators.
+ */
+static int
+grouping_print(struct grouping_state *gs, struct io_state *iop,
+              const CHAR *cp, const CHAR *ep, locale_t locale)
+{
+       const CHAR *cp0 = cp;
+
+       if (io_printandpad(iop, cp, ep, gs->lead, zeroes, locale))
+               return (-1);
+       cp += gs->lead;
+       while (gs->nseps > 0 || gs->nrepeats > 0) {
+               if (gs->nrepeats > 0)
+                       gs->nrepeats--;
+               else {
+                       gs->grouping--;
+                       gs->nseps--;
+               }
+               if (io_print(iop, gs->thousands_sep, gs->thousep_len, locale))
+                       return (-1);
+               if (io_printandpad(iop, cp, ep, *gs->grouping, zeroes, locale))
+                       return (-1);
+               cp += *gs->grouping;
+       }
+       if (cp > ep)
+               cp = ep;
+       return (cp - cp0);
+}
+
+/*
+ * Flush out all the vectors defined by the given uio,
+ * then reset it so that it can be reused.
+ */
+static int
+__sprint(FILE *fp, struct __suio *uio, locale_t locale)
+{
+       int err;
+
+       if (uio->uio_resid == 0) {
+               uio->uio_iovcnt = 0;
+               return (0);
+       }
+       err = __sfvwrite(fp, uio);
+       uio->uio_resid = 0;
+       uio->uio_iovcnt = 0;
+       return (err);
+}
+
+/*
+ * Helper function for `fprintf to unbuffered unix file': creates a
+ * temporary buffer.  We only work on write-only files; this avoids
+ * worries about ungetc buffers and so forth.
+ */
+static int
+__sbprintf(FILE *fp, locale_t locale, const char *fmt, va_list ap)
+{
+       int ret;
+       FILE fake = FAKE_FILE;
+       unsigned char buf[BUFSIZ];
+
+       /* XXX This is probably not needed. */
+       if (prepwrite(fp) != 0)
+               return (EOF);
+
+       /* copy the important variables */
+       fake._flags = fp->_flags & ~__SNBF;
+       fake._file = fp->_file;
+       fake._cookie = fp->_cookie;
+       fake._write = fp->_write;
+       fake._orientation = fp->_orientation;
+       fake._mbstate = fp->_mbstate;
+
+       /* set up the buffer */
+       fake._bf._base = fake._p = buf;
+       fake._bf._size = fake._w = sizeof(buf);
+       fake._lbfsize = 0;      /* not actually used, but Just In Case */
+
+       /* do the work, then copy any error status */
+       ret = __vfprintf(&fake, locale, fmt, ap);
+       if (ret >= 0 && __fflush(&fake))
+               ret = EOF;
+       if (fake._flags & __SERR)
+               fp->_flags |= __SERR;
+       return (ret);
+}
+
+/*
+ * Convert a wide character string argument for the %ls format to a multibyte
+ * string representation. If not -1, prec specifies the maximum number of
+ * bytes to output, and also means that we can't assume that the wide char.
+ * string ends is null-terminated.
+ */
+static char *
+__wcsconv(wchar_t *wcsarg, int prec)
+{
+       static const mbstate_t initial;
+       mbstate_t mbs;
+       char buf[MB_LEN_MAX];
+       wchar_t *p;
+       char *convbuf;
+       size_t clen, nbytes;
+
+       /* Allocate space for the maximum number of bytes we could output. */
+       if (prec < 0) {
+               p = wcsarg;
+               mbs = initial;
+               nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
+               if (nbytes == (size_t)-1)
+                       return (NULL);
+       } else {
+               /*
+                * Optimisation: if the output precision is small enough,
+                * just allocate enough memory for the maximum instead of
+                * scanning the string.
+                */
+               if (prec < 128)
+                       nbytes = prec;
+               else {
+                       nbytes = 0;
+                       p = wcsarg;
+                       mbs = initial;
+                       for (;;) {
+                               clen = wcrtomb(buf, *p++, &mbs);
+                               if (clen == 0 || clen == (size_t)-1 ||
+                                   nbytes + clen > prec)
+                                       break;
+                               nbytes += clen;
+                       }
+               }
+       }
+       if ((convbuf = malloc(nbytes + 1)) == NULL)
+               return (NULL);
+
+       /* Fill the output buffer. */
+       p = wcsarg;
+       mbs = initial;
+       if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p,
+           nbytes, &mbs)) == (size_t)-1) {
+               free(convbuf);
+               return (NULL);
+       }
+       convbuf[nbytes] = '\0';
+       return (convbuf);
+}
+
+/*
+ * MT-safe version
+ */
+int
+vfprintf_l(FILE * __restrict fp, locale_t locale, const char * __restrict fmt0,
+               va_list ap)
+{
+       int ret;
+       FIX_LOCALE(locale);
+
+       FLOCKFILE_CANCELSAFE(fp);
+       /* optimise fprintf(stderr) (and other unbuffered Unix files) */
+       if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
+           fp->_file >= 0)
+               ret = __sbprintf(fp, locale, fmt0, ap);
+       else
+               ret = __vfprintf(fp, locale, fmt0, ap);
+       FUNLOCKFILE_CANCELSAFE();
+       return (ret);
+}
+int
+vfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap)
+{
+       return vfprintf_l(fp, __get_locale(), fmt0, ap);
+}
+
+/*
+ * The size of the buffer we use as scratch space for integer
+ * conversions, among other things.  We need enough space to
+ * write a uintmax_t in octal (plus one byte).
+ */
+#if UINTMAX_MAX <= UINT64_MAX
+#define        BUF     32
+#else
+#error "BUF must be large enough to format a uintmax_t"
+#endif
+
+/*
+ * Non-MT-safe version
+ */
+int
+__vfprintf(FILE *fp, locale_t locale, const char *fmt0, va_list ap)
+{
+       char *fmt;              /* format string */
+       int ch;                 /* character from fmt */
+       int n, n2;              /* handy integer (short term usage) */
+       char *cp;               /* handy char pointer (short term usage) */
+       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 */
+       int saved_errno;
+       char sign;              /* sign prefix (' ', '+', '-', or \0) */
+       struct grouping_state gs; /* thousands' grouping info */
+
+#ifndef NO_FLOATING_POINT
+       /*
+        * 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;    /* locale specific decimal point */
+       int decpt_len;          /* length of decimal_point */
+       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 ndig;               /* actual number of digits returned by dtoa */
+       char expstr[MAXEXPDIG+2];       /* buffer for exponent string: e+ZZZ */
+       char *dtoaresult;       /* buffer allocated by dtoa */
+#endif
+       u_long  ulval;          /* integer arguments %[diouxX] */
+       uintmax_t ujval;        /* %j, %ll, %q, %t, %z integers */
+       int base;               /* base for [diouxX] conversion */
+       int dprec;              /* a copy of prec if [diouxX], 0 otherwise */
+       int realsz;             /* field size expanded by dprec, sign, etc */
+       int size;               /* size of converted field or string */
+       int prsize;             /* max size of printed field */
+       const char *xdigs;      /* digits for %[xX] conversion */
+       struct io_state io;     /* I/O buffering state */
+       char buf[BUF];          /* buffer with space for digits of uintmax_t */
+       char 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];
+       int nextarg;            /* 1-based argument index */
+       va_list orgap;          /* original argument pointer */
+       char *convbuf;          /* wide to multibyte conversion result */
+       int savserr;
+
+       static const char xdigs_lower[16] = "0123456789abcdef";
+       static const char xdigs_upper[16] = "0123456789ABCDEF";
+
+       /* BEWARE, these `goto error' on error. */
+#define        PRINT(ptr, len) { \
+       if (io_print(&io, (ptr), (len), locale))        \
+               goto error; \
+}
+#define        PAD(howmany, with) { \
+       if (io_pad(&io, (howmany), (with), locale)) \
+               goto error; \
+}
+#define        PRINTANDPAD(p, ep, len, with) { \
+       if (io_printandpad(&io, (p), (ep), (len), (with), locale)) \
+               goto error; \
+}
+#define        FLUSH() { \
+       if (io_flush(&io, locale)) \
+               goto error; \
+}
+
+       /*
+        * Get the argument indexed by nextarg.   If the argument table is
+        * built, use it to get the argument.  If its not, get the next
+        * argument (and arguments must be gotten sequentially).
+        */
+#define GETARG(type) \
+       ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
+           (nextarg++, va_arg(ap, type)))
+
+       /*
+        * To extend shorts properly, we need both signed and unsigned
+        * argument extraction methods.
+        */
+#define        SARG() \
+       (flags&LONGINT ? GETARG(long) : \
+           flags&SHORTINT ? (long)(short)GETARG(int) : \
+           flags&CHARINT ? (long)(signed char)GETARG(int) : \
+           (long)GETARG(int))
+#define        UARG() \
+       (flags&LONGINT ? GETARG(u_long) : \
+           flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
+           flags&CHARINT ? (u_long)(u_char)GETARG(int) : \
+           (u_long)GETARG(u_int))
+#define        INTMAX_SIZE     (INTMAXT|SIZET|PTRDIFFT|LLONGINT)
+#define SJARG() \
+       (flags&INTMAXT ? GETARG(intmax_t) : \
+           flags&SIZET ? (intmax_t)GETARG(ssize_t) : \
+           flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \
+           (intmax_t)GETARG(long long))
+#define        UJARG() \
+       (flags&INTMAXT ? GETARG(uintmax_t) : \
+           flags&SIZET ? (uintmax_t)GETARG(size_t) : \
+           flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \
+           (uintmax_t)GETARG(unsigned long long))
+
+       /*
+        * Get * arguments, including the form *nn$.  Preserve the nextarg
+        * that the argument can be gotten once the type is determined.
+        */
+#define GETASTER(val) \
+       n2 = 0; \
+       cp = fmt; \
+       while (is_digit(*cp)) { \
+               n2 = 10 * n2 + to_digit(*cp); \
+               cp++; \
+       } \
+       if (*cp == '$') { \
+               int hold = nextarg; \
+               if (argtable == NULL) { \
+                       argtable = statargtable; \
+                       if (__find_arguments (fmt0, orgap, &argtable)) { \
+                               ret = EOF; \
+                               goto error; \
+                       } \
+               } \
+               nextarg = n2; \
+               val = GETARG (int); \
+               nextarg = hold; \
+               fmt = ++cp; \
+       } else { \
+               val = GETARG (int); \
+       }
+
+       if (__use_xprintf == 0 && getenv("USE_XPRINTF"))
+               __use_xprintf = 1;
+       if (__use_xprintf > 0)
+               return (__xvprintf(fp, fmt0, ap));
+
+       /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
+       if (prepwrite(fp) != 0) {
+               errno = EBADF;
+               return (EOF);
+       }
+
+       savserr = fp->_flags & __SERR;
+       fp->_flags &= ~__SERR;
+
+       saved_errno = errno;
+       convbuf = NULL;
+       fmt = (char *)fmt0;
+       argtable = NULL;
+       nextarg = 1;
+       va_copy(orgap, ap);
+       io_init(&io, fp);
+       ret = 0;
+#ifndef NO_FLOATING_POINT
+       dtoaresult = NULL;
+       decimal_point = localeconv_l(locale)->decimal_point;
+       /* The overwhelmingly common case is decpt_len == 1. */
+       decpt_len = (decimal_point[1] == '\0' ? 1 : strlen(decimal_point));
+#endif
+
+       /*
+        * Scan the format for conversions (`%' character).
+        */
+       for (;;) {
+               for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
+                       /* void */;
+               if ((n = fmt - cp) != 0) {
+                       if ((unsigned)ret + n > INT_MAX) {
+                               ret = EOF;
+                               errno = EOVERFLOW;
+                               goto error;
+                       }
+                       PRINT(cp, n);
+                       ret += n;
+               }
+               if (ch == '\0')
+                       goto done;
+               fmt++;          /* skip over '%' */
+
+               flags = 0;
+               dprec = 0;
+               width = 0;
+               prec = -1;
+               gs.grouping = NULL;
+               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 '*':
+                       /*-
+                        * ``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;
+                       width = -width;
+                       /* FALLTHROUGH */
+               case '-':
+                       flags |= LADJUST;
+                       goto rflag;
+               case '+':
+                       sign = '+';
+                       goto rflag;
+               case '\'':
+                       flags |= GROUPING;
+                       goto rflag;
+               case '.':
+                       if ((ch = *fmt++) == '*') {
+                               GETASTER (prec);
+                               goto rflag;
+                       }
+                       prec = 0;
+                       while (is_digit(ch)) {
+                               prec = 10 * prec + to_digit(ch);
+                               ch = *fmt++;
+                       }
+                       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 {
+                               n = 10 * n + to_digit(ch);
+                               ch = *fmt++;
+                       } while (is_digit(ch));
+                       if (ch == '$') {
+                               nextarg = n;
+                               if (argtable == NULL) {
+                                       argtable = statargtable;
+                                       if (__find_arguments (fmt0, orgap,
+                                                             &argtable)) {
+                                               ret = EOF;
+                                               goto error;
+                                       }
+                               }
+                               goto rflag;
+                       }
+                       width = n;
+                       goto reswitch;
+#ifndef NO_FLOATING_POINT
+               case 'L':
+                       flags |= LONGDBL;
+                       goto rflag;
+#endif
+               case 'h':
+                       if (flags & SHORTINT) {
+                               flags &= ~SHORTINT;
+                               flags |= CHARINT;
+                       } else
+                               flags |= SHORTINT;
+                       goto rflag;
+               case 'j':
+                       flags |= INTMAXT;
+                       goto rflag;
+               case 'l':
+                       if (flags & LONGINT) {
+                               flags &= ~LONGINT;
+                               flags |= LLONGINT;
+                       } else
+                               flags |= LONGINT;
+                       goto rflag;
+               case 'q':
+                       flags |= LLONGINT;      /* not necessarily */
+                       goto rflag;
+               case 't':
+                       flags |= PTRDIFFT;
+                       goto rflag;
+               case 'z':
+                       flags |= SIZET;
+                       goto rflag;
+               case 'C':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'c':
+                       if (flags & LONGINT) {
+                               static const mbstate_t initial;
+                               mbstate_t mbs;
+                               size_t mbseqlen;
+
+                               mbs = initial;
+                               mbseqlen = wcrtomb(cp = buf,
+                                   (wchar_t)GETARG(wint_t), &mbs);
+                               if (mbseqlen == (size_t)-1) {
+                                       fp->_flags |= __SERR;
+                                       goto error;
+                               }
+                               size = (int)mbseqlen;
+                       } else {
+                               *(cp = buf) = GETARG(int);
+                               size = 1;
+                       }
+                       sign = '\0';
+                       break;
+               case 'D':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'd':
+               case 'i':
+                       if (flags & INTMAX_SIZE) {
+                               ujval = SJARG();
+                               if ((intmax_t)ujval < 0) {
+                                       ujval = -ujval;
+                                       sign = '-';
+                               }
+                       } else {
+                               ulval = SARG();
+                               if ((long)ulval < 0) {
+                                       ulval = -ulval;
+                                       sign = '-';
+                               }
+                       }
+                       base = 10;
+                       goto number;
+#ifndef NO_FLOATING_POINT
+               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 != NULL)
+                               freedtoa(dtoaresult);
+                       if (flags & LONGDBL) {
+                               fparg.ldbl = GETARG(long double);
+                               dtoaresult = cp =
+                                   __hldtoa(fparg.ldbl, xdigs, prec,
+                                   &expt, &signflag, &dtoaend);
+                       } else {
+                               fparg.dbl = GETARG(double);
+                               dtoaresult = cp =
+                                   __hdtoa(fparg.dbl, xdigs, prec,
+                                   &expt, &signflag, &dtoaend);
+                       }
+                       if (prec < 0)
+                               prec = dtoaend - cp;
+                       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 != NULL)
+                               freedtoa(dtoaresult);
+                       if (flags & LONGDBL) {
+                               fparg.ldbl = GETARG(long double);
+                               dtoaresult = cp =
+                                   __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
+                                   &expt, &signflag, &dtoaend);
+                       } else {
+                               fparg.dbl = GETARG(double);
+                               dtoaresult = cp =
+                                   dtoa(fparg.dbl, expchar ? 2 : 3, prec,
+                                   &expt, &signflag, &dtoaend);
+                               if (expt == 9999)
+                                       expt = INT_MAX;
+                       }
+fp_common:
+                       if (signflag)
+                               sign = '-';
+                       if (expt == INT_MAX) {  /* inf or nan */
+                               if (*cp == 'N') {
+                                       cp = (ch >= 'a') ? "nan" : "NAN";
+                                       sign = '\0';
+                               } else
+                                       cp = (ch >= 'a') ? "inf" : "INF";
+                               size = 3;
+                               flags &= ~ZEROPAD;
+                               break;
+                       }
+                       flags |= FPT;
+                       ndig = dtoaend - cp;
+                       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 += decpt_len;
+                       } 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 + decpt_len;
+                               if ((flags & GROUPING) && expt > 0)
+                                       size += grouping_init(&gs, expt, locale);
+                       }
+                       break;
+#endif /* !NO_FLOATING_POINT */
+               case 'm':
+                       cp = strerror(saved_errno);
+                       size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
+                       sign = '\0';
+                       break;
+               case 'n':
+                       /*
+                        * Assignment-like behavior is specified if the
+                        * value overflows or is otherwise unrepresentable.
+                        * C99 says to use `signed char' for %hhn conversions.
+                        */
+                       if (flags & LLONGINT)
+                               *GETARG(long long *) = ret;
+                       else if (flags & SIZET)
+                               *GETARG(ssize_t *) = (ssize_t)ret;
+                       else if (flags & PTRDIFFT)
+                               *GETARG(ptrdiff_t *) = ret;
+                       else if (flags & INTMAXT)
+                               *GETARG(intmax_t *) = ret;
+                       else if (flags & LONGINT)
+                               *GETARG(long *) = ret;
+                       else if (flags & SHORTINT)
+                               *GETARG(short *) = ret;
+                       else if (flags & CHARINT)
+                               *GETARG(signed char *) = ret;
+                       else
+                               *GETARG(int *) = ret;
+                       continue;       /* no output */
+               case 'O':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'o':
+                       if (flags & INTMAX_SIZE)
+                               ujval = UJARG();
+                       else
+                               ulval = UARG();
+                       base = 8;
+                       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
+                        */
+                       ujval = (uintmax_t)(uintptr_t)GETARG(void *);
+                       base = 16;
+                       xdigs = xdigs_lower;
+                       flags = flags | INTMAXT;
+                       ox[1] = 'x';
+                       goto nosign;
+               case 'S':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 's':
+                       if (flags & LONGINT) {
+                               wchar_t *wcp;
+
+                               if (convbuf != NULL)
+                                       free(convbuf);
+                               if ((wcp = GETARG(wchar_t *)) == NULL)
+                                       cp = "(null)";
+                               else {
+                                       convbuf = __wcsconv(wcp, prec);
+                                       if (convbuf == NULL) {
+                                               fp->_flags |= __SERR;
+                                               goto error;
+                                       }
+                                       cp = convbuf;
+                               }
+                       } else if ((cp = GETARG(char *)) == NULL)
+                               cp = "(null)";
+                       size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
+                       sign = '\0';
+                       break;
+               case 'U':
+                       flags |= LONGINT;
+                       /*FALLTHROUGH*/
+               case 'u':
+                       if (flags & INTMAX_SIZE)
+                               ujval = UJARG();
+                       else
+                               ulval = UARG();
+                       base = 10;
+                       goto nosign;
+               case 'X':
+                       xdigs = xdigs_upper;
+                       goto hex;
+               case 'x':
+                       xdigs = xdigs_lower;
+hex:
+                       if (flags & INTMAX_SIZE)
+                               ujval = UJARG();
+                       else
+                               ulval = UARG();
+                       base = 16;
+                       /* leading 0x/X only if non-zero */
+                       if (flags & ALT &&
+                           (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
+                               ox[1] = ch;
+
+                       flags &= ~GROUPING;
+                       /* 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
+                        *
+                        * ``The C Standard is clear enough as is.  The call
+                        * printf("%#.0o", 0) should print 0.''
+                        *      -- Defect Report #151
+                        */
+                       cp = buf + BUF;
+                       if (flags & INTMAX_SIZE) {
+                               if (ujval != 0 || prec != 0 ||
+                                   (flags & ALT && base == 8))
+                                       cp = __ujtoa(ujval, cp, base,
+                                           flags & ALT, xdigs);
+                       } else {
+                               if (ulval != 0 || prec != 0 ||
+                                   (flags & ALT && base == 8))
+                                       cp = __ultoa(ulval, cp, base,
+                                           flags & ALT, xdigs);
+                       }
+                       size = buf + BUF - cp;
+                       if (size > BUF) /* should never happen */
+                               abort();
+                       if ((flags & GROUPING) && size != 0)
+                               size += grouping_init(&gs, size, locale);
+                       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 [diouxX] 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;
+
+               prsize = width > realsz ? width : realsz;
+               if ((unsigned)ret + prsize > INT_MAX) {
+                       ret = EOF;
+                       errno = EOVERFLOW;
+                       goto error;
+               }
+
+               /* 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);
+
+               /* the string or number proper */
+#ifndef NO_FLOATING_POINT
+               if ((flags & FPT) == 0) {
+#endif
+                       /* leading zeroes from decimal precision */
+                       PAD(dprec - size, zeroes);
+                       if (gs.grouping) {
+                               if (grouping_print(&gs, &io, cp, buf+BUF, locale) < 0)
+                                       goto error;
+                       } else {
+                               PRINT(cp, size);
+                       }
+#ifndef NO_FLOATING_POINT
+               } else {        /* glue together f_p fragments */
+                       if (!expchar) { /* %[fF] or sufficiently short %[gG] */
+                               if (expt <= 0) {
+                                       PRINT(zeroes, 1);
+                                       if (prec || flags & ALT)
+                                               PRINT(decimal_point,decpt_len);
+                                       PAD(-expt, zeroes);
+                                       /* already handled initial 0's */
+                                       prec += expt;
+                               } else {
+                                       if (gs.grouping) {
+                                               n = grouping_print(&gs, &io,
+                                                   cp, dtoaend, locale);
+                                               if (n < 0)
+                                                       goto error;
+                                               cp += n;
+                                       } else {
+                                               PRINTANDPAD(cp, dtoaend,
+                                                   expt, zeroes);
+                                               cp += expt;
+                                       }
+                                       if (prec || flags & ALT)
+                                               PRINT(decimal_point,decpt_len);
+                               }
+                               PRINTANDPAD(cp, dtoaend, prec, zeroes);
+                       } else {        /* %[eE] or sufficiently long %[gG] */
+                               if (prec > 1 || flags & ALT) {
+                                       PRINT(cp++, 1);
+                                       PRINT(decimal_point, decpt_len);
+                                       PRINT(cp, ndig-1);
+                                       PAD(prec - ndig, zeroes);
+                               } else  /* XeYYY */
+                                       PRINT(cp, 1);
+                               PRINT(expstr, expsize);
+                       }
+               }
+#endif
+               /* left-adjusting padding (always blank) */
+               if (flags & LADJUST)
+                       PAD(width - realsz, blanks);
+
+               /* finally, adjust ret */
+               ret += prsize;
+
+               FLUSH();        /* copy out the I/O vectors */
+       }
+done:
+       FLUSH();
+error:
+       va_end(orgap);
+#ifndef NO_FLOATING_POINT
+       if (dtoaresult != NULL)
+               freedtoa(dtoaresult);
+#endif
+       if (convbuf != NULL)
+               free(convbuf);
+       if (__sferror(fp))
+               ret = EOF;
+       else
+               fp->_flags |= savserr;
+       if ((argtable != NULL) && (argtable != statargtable))
+               free (argtable);
+       return (ret);
+       /* NOTREACHED */
+}
+