Mon Apr 1 11:39:10 Ulrich Drepper <drepper@gnu.ai.mit.edu>
authorroland <roland>
Tue, 2 Apr 1996 13:41:34 +0000 (13:41 +0000)
committerroland <roland>
Tue, 2 Apr 1996 13:41:34 +0000 (13:41 +0000)
* stdio-common/vfprintf.c (vfprintf): Don't increment format
string pointer twice after seeing `*'.
Sun Mar 31 18:07:32 1996  Ulrich Drepper  <drepper@gnu.ai.mit.edu>

* stdio-common/vfprintf.c: New and fast implementation.

stdio-common/vfprintf.c

index d6b9f9a..26b31a6 100644 (file)
@@ -13,128 +13,162 @@ Library General Public License for more details.
 
 You should have received a copy of the GNU Library General Public
 License along with the GNU C Library; see the file COPYING.LIB.  If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA.  */
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 #include <ctype.h>
-#include <errno.h>
-#include <float.h>
 #include <limits.h>
-#include <math.h>
 #include <printf.h>
 #include <stdarg.h>
 #include <stdlib.h>
-#include <string.h>
-#include <printf.h>
-#include <stddef.h>
+#include <wchar.h>
 #include "_itoa.h"
 #include "../locale/localeinfo.h"
 
+/* This code is shared between the standard stdio implementation found
+   in GNU C library and the libio implementation originally found in
+   GNU libg++.
+
+   Beside this it is also shared between the normal and wide character
+   implementation as defined in ISO/IEC 9899:1990/Amendment 1:1995.  */
+
+#ifndef COMPILE_WPRINTF
+# define CHAR_T                char
+# define UCHAR_T       unsigned char
+# define INT_T         int
+# define L_(Str)       Str
+# define ISDIGIT(Ch)   isdigit (Ch)
+
+# ifdef USE_IN_LIBIO
+#  define PUT(F, S, N) _IO_sputn (F, S, N)
+#  define PAD(Padchar)                                                       \
+  if (width > 0)                                                             \
+    done += _IO_padn (s, Padchar, width)
+# else
+#  define PUTC(C, F)   putc (C, F)
+ssize_t __printf_pad __P ((FILE *, char pad, size_t n));
+# define PAD(Padchar)                                                        \
+  if (width > 0)                                                             \
+    { if (__printf_pad (s, Padchar, width) == -1)                            \
+       return -1; else done += width; }
+# endif
+#else
+# define vfprintf      vfwprintf
+# define CHAR_T                wchar_t
+# define UCHAR_T       uwchar_t
+# define INT_T         wint_t
+# define L_(Str)       L##Str
+# define ISDIGIT(Ch)   iswdigit (Ch)
+
+# ifdef USE_IN_LIBIO
+# define PUT(F, S, N)  _IO_sputn (F, S, N)
+# define PAD(Padchar)                                                        \
+  if (width > 0)                                                             \
+    done += _IO_wpadn (s, Padchar, width)
+# else
+#  define PUTC(C, F)   wputc (C, F)
+ssize_t __wprintf_pad __P ((FILE *, wchar_t pad, size_t n));
+# define PAD(Padchar)                                                        \
+  if (width > 0)                                                             \
+    { if (__wprintf_pad (s, Padchar, width) == -1)                           \
+       return -1; else done += width; }
+# endif
+#endif
+
 /* Include the shared code for parsing the format string.  */
 #include "printf-parse.h"
 
 
-/* This function from the GNU C library is also used in libio.
-   To compile for use in libio, compile with -DUSE_IN_LIBIO.  */
-
 #ifdef USE_IN_LIBIO
 /* This code is for use in libio.  */
-#include <libioP.h>
-#define PUT(f, s, n)   _IO_sputn (f, s, n)
-#define PAD(padchar)                                                         \
-  if (specs[cnt].info.width > 0)                                             \
-    done += _IO_padn (s, padchar, specs[cnt].info.width)
-#define PUTC(c, f)     _IO_putc (c, f)
-#define vfprintf       _IO_vfprintf
-#define size_t         _IO_size_t
-#define FILE           _IO_FILE
-#define va_list                _IO_va_list
-#undef BUFSIZ
-#define BUFSIZ         _IO_BUFSIZ
-#define ARGCHECK(s, format)                                                  \
+# include <libioP.h>
+# define PUTC(C, F)    _IO_putc (C, F)
+# define vfprintf      _IO_vfprintf
+# define size_t                _IO_size_t
+# define FILE          _IO_FILE
+# define va_list       _IO_va_list
+# undef        BUFSIZ
+# define BUFSIZ                _IO_BUFSIZ
+# define ARGCHECK(S, Format)                                                 \
   do                                                                         \
     {                                                                        \
       /* Check file argument for consistence.  */                            \
-      CHECK_FILE (s, -1);                                                    \
-      if (s->_flags & _IO_NO_WRITES || format == NULL)                       \
+      CHECK_FILE (S, -1);                                                    \
+      if (S->_flags & _IO_NO_WRITES || Format == NULL)                       \
        {                                                                     \
          MAYBE_SET_EINVAL;                                                   \
          return -1;                                                          \
        }                                                                     \
     } while (0)
-#define UNBUFFERED_P(s)        ((s)->_IO_file_flags & _IO_UNBUFFERED)
+# define UNBUFFERED_P(S) ((S)->_IO_file_flags & _IO_UNBUFFERED)
 #else /* ! USE_IN_LIBIO */
 /* This code is for use in the GNU C library.  */
-#include <stdio.h>
-#define PUTC(c, f)     putc (c, f)
-#define PUT(f, s, n)   fwrite (s, 1, n, f)
-ssize_t __printf_pad __P ((FILE *, char pad, size_t n));
-#define PAD(padchar)                                                         \
-  if (specs[cnt].info.width > 0)                                             \
-    { if (__printf_pad (s, padchar, specs[cnt].info.width) == -1)            \
-       return -1; else done += specs[cnt].info.width; }
-#define ARGCHECK(s, format) \
+# include <stdio.h>
+# define PUT(F, S, N)  fwrite (S, 1, N, F)
+# define ARGCHECK(S, Format)                                                 \
   do                                                                         \
     {                                                                        \
       /* Check file argument for consistence.  */                            \
-      if (!__validfp(s) || !s->__mode.__write || format == NULL)             \
+      if (!__validfp(S) || !S->__mode.__write || Format == NULL)             \
        {                                                                     \
          errno = EINVAL;                                                     \
          return -1;                                                          \
        }                                                                     \
-      if (!s->__seen)                                                        \
+      if (!S->__seen)                                                        \
        {                                                                     \
-         if (__flshfp (s, EOF) == EOF)                                       \
+         if (__flshfp (S, EOF) == EOF)                                       \
            return -1;                                                        \
        }                                                                     \
-    } while (0)
-#define UNBUFFERED_P(s)        ((s)->__buffer == NULL)
+    }                                                                        \
+   while (0)
+# define UNBUFFERED_P(s) ((s)->__buffer == NULL)
 #endif /* USE_IN_LIBIO */
 
 
-#define        outchar(x)                                                            \
+#define        outchar(Ch)                                                           \
   do                                                                         \
     {                                                                        \
-      register const int outc = (x);                                         \
+      register const int outc = (Ch);                                        \
       if (PUTC (outc, s) == EOF)                                             \
        return -1;                                                            \
       else                                                                   \
        ++done;                                                               \
-    } while (0)
+    }                                                                        \
+  while (0)
 
-#define outstring(string, len)                                               \
+#define outstring(String, Len)                                               \
   do                                                                         \
     {                                                                        \
-      if (len > 20)                                                          \
-       {                                                                     \
-         if (PUT (s, string, len) != len)                                    \
-           return -1;                                                        \
-         done += len;                                                        \
-       }                                                                     \
-      else                                                                   \
-       {                                                                     \
-         register const char *cp = string;                                   \
-         register int l = len;                                               \
-         while (l-- > 0)                                                     \
-           outchar (*cp++);                                                  \
-       }                                                                     \
-    } while (0)
+      if (PUT (s, String, Len) != Len)                                       \
+       return -1;                                                            \
+      done += Len;                                                           \
+    }                                                                        \
+  while (0)
 
-/* Helper function to provide temporary buffering for unbuffered streams.  */
-static int buffered_vfprintf __P ((FILE *stream, const char *fmt, va_list));
+/* For handling long_double and longlong we use the same flag.  */
+#ifndef is_longlong
+# define is_longlong is_long_double
+#endif
+
+
+/* Global variables.  */
+static const char null[] = "(null)";
 
-static printf_function printf_unknown;
 
-extern printf_function **__printf_function_table;
+/* Helper function to provide temporary buffering for unbuffered streams.  */
+static int buffered_vfprintf __P ((FILE *stream, const CHAR_T *fmt, va_list));
+
+/* Handle unknown format specifier.  */
+static int printf_unknown __P ((FILE *, const struct printf_info *,
+                               const void *const *));
 
-static char *group_number __P ((char *, char *, const char *, wchar_t));
+/* Group digits of number string.  */
+static char *group_number __P ((CHAR_T *, CHAR_T *, const CHAR_T *, wchar_t));
 
 
+/* The function itself.  */
 int
-vfprintf (s, format, ap)
-    register FILE *s;
-    const char *format;
-    va_list ap;
+vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 {
   /* The character used as thousands separator.  */
   wchar_t thousands_sep;
@@ -142,35 +176,613 @@ vfprintf (s, format, ap)
   /* The string describing the size of groups of digits.  */
   const char *grouping;
 
-  /* Array with information about the needed arguments.  This has to be
-     dynamically extendable.  */
-  size_t nspecs;
-  size_t nspecs_max;
-  struct printf_spec *specs;
+  /* Place to accumulate the result.  */
+  int done;
 
-  /* The number of arguments the format string requests.  This will
-     determine the size of the array needed to store the argument
-     attributes.  */
-  size_t nargs;
-  int *args_type;
-  union printf_arg *args_value;
-
-  /* Positional parameters refer to arguments directly.  This could also
-     determine the maximum number of arguments.  Track the maximum number.  */
-  size_t max_ref_arg;
+  /* Current character in format string.  */
+  const UCHAR_T *f;
 
   /* End of leading constant string.  */
-  const char *lead_str_end;
+  const UCHAR_T *lead_str_end;
+
+  /* Points to next format specifier.  */
+  const UCHAR_T *end_of_spec;
+
+  /* Buffer intermediate results.  */
+  char work_buffer[1000];
+#define workend (&work_buffer[sizeof (work_buffer) - 1])
+
+  /* State for restartable multibyte character handling functions.  */
+  mbstate_t mbstate;
+
+  /* We have to save the original argument pointer.  */
+  va_list ap_save;
 
-  /* Number of characters written.  */
-  register size_t done = 0;
+  /* Count number of specifiers we already processed.  */
+  int nspecs_done;
+
+
+  /* This table maps a character into a number representing a
+     class.  In each step there is a destination label for each
+     class.  */
+  static const int jump_table[] =
+  {
+    /* ' ' */  1,            0,            0, /* '#' */  4,
+              0, /* '%' */ 14,            0, /* '\''*/  6,
+              0,            0, /* '*' */  7, /* '+' */  2,
+              0, /* '-' */  3, /* '.' */  9,            0,
+    /* '0' */  5, /* '1' */  8, /* '2' */  8, /* '3' */  8,
+    /* '4' */  8, /* '5' */  8, /* '6' */  8, /* '7' */  8,
+    /* '8' */  8, /* '9' */  8,            0,            0,
+              0,            0,            0,            0,
+              0,            0,            0,            0,
+              0, /* 'E' */ 19,            0, /* 'G' */ 19,
+              0,            0,            0,            0,
+    /* 'L' */ 12,            0,            0,            0,
+              0,            0,            0,            0,
+              0,            0,            0,            0,
+    /* 'X' */ 18,            0, /* 'Z' */ 13,            0,
+              0,            0,            0,            0,
+              0,            0,            0, /* 'c' */ 20,
+    /* 'd' */ 15, /* 'e' */ 19, /* 'f' */ 19, /* 'g' */ 19,
+    /* 'h' */ 10, /* 'i' */ 15,            0,            0,
+    /* 'l' */ 11, /* 'm' */ 24, /* 'n' */ 23, /* 'o' */ 17,
+    /* 'p' */ 22, /* 'q' */ 12,            0, /* 's' */ 21,
+              0, /* 'u' */ 16,            0,            0,
+    /* 'x' */ 18
+  };
+
+#define NOT_IN_JUMP_RANGE(Ch) ((Ch) < ' ' || (Ch) > 'x')
+#define CHAR_CLASS(Ch) (jump_table[(int) (Ch) - ' '])
+#define JUMP(ChExpr, table)                                                  \
+      do                                                                     \
+       {                                                                     \
+         const void *ptr;                                                    \
+         spec = (ChExpr);                                                    \
+         ptr = NOT_IN_JUMP_RANGE (spec) ? REF (form_unknown)                 \
+           : table[CHAR_CLASS (spec)];                                       \
+         goto *ptr;                                                          \
+       }                                                                     \
+      while (0)
+
+#define STEP0_3_TABLE                                                        \
+    /* Step 0: at the beginning.  */                                         \
+    static const void *step0_jumps[25] =                                     \
+    {                                                                        \
+      REF (form_unknown),                                                    \
+      REF (flag_space),                /* for ' ' */                                 \
+      REF (flag_plus),         /* for '+' */                                 \
+      REF (flag_minus),                /* for '-' */                                 \
+      REF (flag_hash),         /* for '<hash>' */                            \
+      REF (flag_zero),         /* for '0' */                                 \
+      REF (flag_quote),                /* for '\'' */                                \
+      REF (width_asterics),    /* for '*' */                                 \
+      REF (width),             /* for '1'...'9' */                           \
+      REF (precision),         /* for '.' */                                 \
+      REF (mod_half),          /* for 'h' */                                 \
+      REF (mod_long),          /* for 'l' */                                 \
+      REF (mod_longlong),      /* for 'L', 'q' */                            \
+      REF (mod_size_t),                /* for 'Z' */                                 \
+      REF (form_percent),      /* for '%' */                                 \
+      REF (form_integer),      /* for 'd', 'i' */                            \
+      REF (form_unsigned),     /* for 'u' */                                 \
+      REF (form_octal),                /* for 'o' */                                 \
+      REF (form_hexa),         /* for 'X', 'x' */                            \
+      REF (form_float),                /* for 'E', 'e', 'f', 'G', 'g' */             \
+      REF (form_character),    /* for 'c' */                                 \
+      REF (form_string),       /* for 's' */                                 \
+      REF (form_pointer),      /* for 'p' */                                 \
+      REF (form_number),       /* for 'n' */                                 \
+      REF (form_strerror)      /* for 'm' */                                 \
+    };                                                                       \
+    /* Step 1: after processing width.  */                                   \
+    static const void *step1_jumps[25] =                                     \
+    {                                                                        \
+      REF (form_unknown),                                                    \
+      REF (form_unknown),      /* for ' ' */                                 \
+      REF (form_unknown),      /* for '+' */                                 \
+      REF (form_unknown),      /* for '-' */                                 \
+      REF (form_unknown),      /* for '<hash>' */                            \
+      REF (form_unknown),      /* for '0' */                                 \
+      REF (form_unknown),      /* for '\'' */                                \
+      REF (form_unknown),      /* for '*' */                                 \
+      REF (form_unknown),      /* for '1'...'9' */                           \
+      REF (precision),         /* for '.' */                                 \
+      REF (mod_half),          /* for 'h' */                                 \
+      REF (mod_long),          /* for 'l' */                                 \
+      REF (mod_longlong),      /* for 'L', 'q' */                            \
+      REF (mod_size_t),                /* for 'Z' */                                 \
+      REF (form_percent),      /* for '%' */                                 \
+      REF (form_integer),      /* for 'd', 'i' */                            \
+      REF (form_unsigned),     /* for 'u' */                                 \
+      REF (form_octal),                /* for 'o' */                                 \
+      REF (form_hexa),         /* for 'X', 'x' */                            \
+      REF (form_float),                /* for 'E', 'e', 'f', 'G', 'g' */             \
+      REF (form_character),    /* for 'c' */                                 \
+      REF (form_string),       /* for 's' */                                 \
+      REF (form_pointer),      /* for 'p' */                                 \
+      REF (form_number),       /* for 'n' */                                 \
+      REF (form_strerror)      /* for 'm' */                                 \
+    };                                                                       \
+    /* Step 2: after processing precision.  */                               \
+    static const void *step2_jumps[25] =                                     \
+    {                                                                        \
+      REF (form_unknown),                                                    \
+      REF (form_unknown),      /* for ' ' */                                 \
+      REF (form_unknown),      /* for '+' */                                 \
+      REF (form_unknown),      /* for '-' */                                 \
+      REF (form_unknown),      /* for '<hash>' */                            \
+      REF (form_unknown),      /* for '0' */                                 \
+      REF (form_unknown),      /* for '\'' */                                \
+      REF (form_unknown),      /* for '*' */                                 \
+      REF (form_unknown),      /* for '1'...'9' */                           \
+      REF (form_unknown),      /* for '.' */                                 \
+      REF (mod_half),          /* for 'h' */                                 \
+      REF (mod_long),          /* for 'l' */                                 \
+      REF (mod_longlong),      /* for 'L', 'q' */                            \
+      REF (mod_size_t),                /* for 'Z' */                                 \
+      REF (form_percent),      /* for '%' */                                 \
+      REF (form_integer),      /* for 'd', 'i' */                            \
+      REF (form_unsigned),     /* for 'u' */                                 \
+      REF (form_octal),                /* for 'o' */                                 \
+      REF (form_hexa),         /* for 'X', 'x' */                            \
+      REF (form_float),                /* for 'E', 'e', 'f', 'G', 'g' */             \
+      REF (form_character),    /* for 'c' */                                 \
+      REF (form_string),       /* for 's' */                                 \
+      REF (form_pointer),      /* for 'p' */                                 \
+      REF (form_number),       /* for 'n' */                                 \
+      REF (form_strerror)      /* for 'm' */                                 \
+    };                                                                       \
+    /* Step 3: after processing first 'l' modifier.  */                              \
+    static const void *step3_jumps[25] =                                     \
+    {                                                                        \
+      REF (form_unknown),                                                    \
+      REF (form_unknown),      /* for ' ' */                                 \
+      REF (form_unknown),      /* for '+' */                                 \
+      REF (form_unknown),      /* for '-' */                                 \
+      REF (form_unknown),      /* for '<hash>' */                            \
+      REF (form_unknown),      /* for '0' */                                 \
+      REF (form_unknown),      /* for '\'' */                                \
+      REF (form_unknown),      /* for '*' */                                 \
+      REF (form_unknown),      /* for '1'...'9' */                           \
+      REF (form_unknown),      /* for '.' */                                 \
+      REF (form_unknown),      /* for 'h' */                                 \
+      REF (mod_longlong),      /* for 'l' */                                 \
+      REF (form_unknown),      /* for 'L', 'q' */                            \
+      REF (form_unknown),      /* for 'Z' */                                 \
+      REF (form_percent),      /* for '%' */                                 \
+      REF (form_integer),      /* for 'd', 'i' */                            \
+      REF (form_unsigned),     /* for 'u' */                                 \
+      REF (form_octal),                /* for 'o' */                                 \
+      REF (form_hexa),         /* for 'X', 'x' */                            \
+      REF (form_float),                /* for 'E', 'e', 'f', 'G', 'g' */             \
+      REF (form_character),    /* for 'c' */                                 \
+      REF (form_string),       /* for 's' */                                 \
+      REF (form_pointer),      /* for 'p' */                                 \
+      REF (form_number),       /* for 'n' */                                 \
+      REF (form_strerror)      /* for 'm' */                                 \
+    }
 
-  /* Running pointer through format string.  */
-  const char *f;
+#define STEP4_TABLE                                                          \
+    /* Step 4: processing format specifier.  */                                      \
+    static const void *step4_jumps[25] =                                     \
+    {                                                                        \
+      REF (form_unknown),                                                    \
+      REF (form_unknown),      /* for ' ' */                                 \
+      REF (form_unknown),      /* for '+' */                                 \
+      REF (form_unknown),      /* for '-' */                                 \
+      REF (form_unknown),      /* for '<hash>' */                            \
+      REF (form_unknown),      /* for '0' */                                 \
+      REF (form_unknown),      /* for '\'' */                                \
+      REF (form_unknown),      /* for '*' */                                 \
+      REF (form_unknown),      /* for '1'...'9' */                           \
+      REF (form_unknown),      /* for '.' */                                 \
+      REF (form_unknown),      /* for 'h' */                                 \
+      REF (form_unknown),      /* for 'l' */                                 \
+      REF (form_unknown),      /* for 'L', 'q' */                            \
+      REF (form_unknown),      /* for 'Z' */                                 \
+      REF (form_percent),      /* for '%' */                                 \
+      REF (form_integer),      /* for 'd', 'i' */                            \
+      REF (form_unsigned),     /* for 'u' */                                 \
+      REF (form_octal),                /* for 'o' */                                 \
+      REF (form_hexa),         /* for 'X', 'x' */                            \
+      REF (form_float),                /* for 'E', 'e', 'f', 'G', 'g' */             \
+      REF (form_character),    /* for 'c' */                                 \
+      REF (form_string),       /* for 's' */                                 \
+      REF (form_pointer),      /* for 'p' */                                 \
+      REF (form_number),       /* for 'n' */                                 \
+      REF (form_strerror)      /* for 'm' */                                 \
+    }
 
-  /* Just a counter.  */
-  int cnt;
 
+#define process_arg(fspec)                                                   \
+      /* Start real work.  We know about all flag and modifiers and          \
+        now process the wanted format specifier.  */                         \
+    LABEL (form_percent):                                                    \
+      /* Write a literal "%".  */                                            \
+      outchar ('%');                                                         \
+      break;                                                                 \
+                                                                             \
+    LABEL (form_integer):                                                    \
+      /* Signed decimal integer.  */                                         \
+      base = 10;                                                             \
+                                                                             \
+      if (is_longlong)                                                       \
+       {                                                                     \
+         long long int signed_number;                                        \
+                                                                             \
+         signed_number = va_arg (ap, long long int);                         \
+                                                                             \
+         is_negative = signed_number < 0;                                    \
+         number.longlong = is_negative ? (- signed_number) : signed_number;  \
+                                                                             \
+         goto LABEL (longlong_number);                                       \
+       }                                                                     \
+      else                                                                   \
+       {                                                                     \
+         long int signed_number;                                             \
+                                                                             \
+         if (is_long)                                                        \
+           signed_number = va_arg (ap, long int);                            \
+         else  /* `short int' will be promoted to `int'.  */                 \
+           signed_number = va_arg (ap, int);                                 \
+                                                                             \
+         is_negative = signed_number < 0;                                    \
+         number.word = is_negative ? (- signed_number) : signed_number;      \
+                                                                             \
+         goto LABEL (number);                                                \
+       }                                                                     \
+      /* NOTREACHED */                                                       \
+                                                                             \
+    LABEL (form_unsigned):                                                   \
+      /* Unsigned decimal integer.  */                                       \
+      base = 10;                                                             \
+      goto LABEL (unsigned_number);                                          \
+      /* NOTREACHED */                                                       \
+                                                                             \
+    LABEL (form_octal):                                                              \
+      /* Unsigned octal integer.  */                                         \
+      base = 8;                                                                      \
+      goto LABEL (unsigned_number);                                          \
+      /* NOTREACHED */                                                       \
+                                                                             \
+    LABEL (form_hexa):                                                       \
+      /* Unsigned hexadecimal integer.  */                                   \
+      base = 16;                                                             \
+                                                                             \
+    LABEL (unsigned_number):     /* Unsigned number of base BASE.  */        \
+                                                                             \
+      /* ANSI specifies the `+' and ` ' flags only for signed                \
+        conversions.  */                                                     \
+      is_negative = 0;                                                       \
+      showsign = 0;                                                          \
+      space = 0;                                                             \
+                                                                             \
+      if (is_longlong)                                                       \
+       {                                                                     \
+         number.longlong = va_arg (ap, unsigned long long int);              \
+                                                                             \
+       LABEL (longlong_number):                                              \
+         if (prec < 0)                                                       \
+           /* Supply a default precision if none was given.  */              \
+           prec = 1;                                                         \
+         else                                                                \
+           /* We have to take care for the '0' flag.  If a precision         \
+              is given it must be ignored.  */                               \
+           pad = ' ';                                                        \
+                                                                             \
+         /* If the precision is 0 and the number is 0 nothing has to         \
+            be written for the number.  */                                   \
+         if (prec == 0 && number.longlong == 0)                              \
+           string = workend;                                                 \
+         else                                                                \
+           {                                                                 \
+             /* Put the number in WORK.  */                                  \
+             string = _itoa (number.longlong, workend + 1, base,             \
+                             spec == 'X');                                   \
+             string -= 1;                                                    \
+             if (group && grouping)                                          \
+               string = group_number (string, workend, grouping,             \
+                                      thousands_sep);                        \
+           }                                                                 \
+         /* Simply further test for num != 0.  */                            \
+         number.word = number.longlong != 0;                                 \
+       }                                                                     \
+      else                                                                   \
+       {                                                                     \
+         if (is_long)                                                        \
+           number.word = va_arg (ap, unsigned long int);                     \
+         else                                                                \
+           number.word = va_arg (ap, unsigned int); /* Promoted.  */         \
+                                                                             \
+       LABEL (number):                                                       \
+         if (prec < 0)                                                       \
+           /* Supply a default precision if none was given.  */              \
+           prec = 1;                                                         \
+         else                                                                \
+           /* We have to take care for the '0' flag.  If a precision         \
+              is given it must be ignored.  */                               \
+           pad = ' ';                                                        \
+                                                                             \
+         /* If the precision is 0 and the number is 0 nothing has to         \
+            be written for the number.  */                                   \
+         if (prec == 0 && number.word == 0)                                  \
+           string = workend;                                                 \
+         else                                                                \
+           {                                                                 \
+             /* Put the number in WORK.  */                                  \
+             string = _itoa_word (number.word, workend + 1, base,            \
+                                  spec == 'X');                              \
+             string -= 1;                                                    \
+             if (group && grouping)                                          \
+               string = group_number (string, workend, grouping,             \
+                                      thousands_sep);                        \
+           }                                                                 \
+       }                                                                     \
+                                                                             \
+      prec -= workend - string;                                                      \
+                                                                             \
+      if (prec > 0)                                                          \
+       /* Add zeros to the precision.  */                                    \
+       while (prec-- > 0)                                                    \
+         *string-- = '0';                                                    \
+      else if (number.word != 0 && alt && base == 8)                         \
+       /* Add octal marker.  */                                              \
+       *string-- = '0';                                                      \
+                                                                             \
+      if (!left)                                                             \
+       {                                                                     \
+         width -= workend - string;                                          \
+                                                                             \
+         if (number.word != 0 && alt && base == 16)                          \
+           /* Account for 0X hex marker.  */                                 \
+           width -= 2;                                                       \
+                                                                             \
+         if (is_negative || showsign || space)                               \
+           --width;                                                          \
+                                                                             \
+         if (pad == '0')                                                     \
+           {                                                                 \
+             while (width-- > 0)                                             \
+               *string-- = '0';                                              \
+                                                                             \
+             if (number.word != 0 && alt && base == 16)                      \
+               {                                                             \
+                 *string-- = spec;                                           \
+                 *string-- = '0';                                            \
+               }                                                             \
+                                                                             \
+             if (is_negative)                                                \
+               *string-- = '-';                                              \
+             else if (showsign)                                              \
+               *string-- = '+';                                              \
+             else if (space)                                                 \
+               *string-- = ' ';                                              \
+           }                                                                 \
+         else                                                                \
+           {                                                                 \
+             if (number.word != 0 && alt && base == 16)                      \
+               {                                                             \
+                 *string-- = spec;                                           \
+                 *string-- = '0';                                            \
+               }                                                             \
+                                                                             \
+             if (is_negative)                                                \
+               *string-- = '-';                                              \
+             else if (showsign)                                              \
+               *string-- = '+';                                              \
+             else if (space)                                                 \
+               *string-- = ' ';                                              \
+                                                                             \
+             while (width-- > 0)                                             \
+               *string-- = ' ';                                              \
+           }                                                                 \
+                                                                             \
+         outstring (string + 1, workend - string);                           \
+                                                                             \
+         break;                                                              \
+       }                                                                     \
+      else                                                                   \
+       {                                                                     \
+         if (number.word != 0 && alt && base == 16)                          \
+           {                                                                 \
+             *string-- = spec;                                               \
+             *string-- = '0';                                                \
+           }                                                                 \
+                                                                             \
+         if (is_negative)                                                    \
+           *string-- = '-';                                                  \
+         else if (showsign)                                                  \
+           *string-- = '+';                                                  \
+         else if (space)                                                     \
+           *string-- = ' ';                                                  \
+                                                                             \
+         width -= workend - string;                                          \
+         outstring (string + 1, workend - string);                           \
+                                                                             \
+         PAD (' ');                                                          \
+         break;                                                              \
+       }                                                                     \
+                                                                             \
+    LABEL (form_float):                                                              \
+      {                                                                              \
+       /* Floating-point number.  This is handled by printf_fp.c.  */        \
+       extern int __printf_fp __P ((FILE *, const struct printf_info *,      \
+                                    const void **const));                    \
+       const void *ptr;                                                      \
+       int function_done;                                                    \
+                                                                             \
+       if (is_long_double)                                                   \
+         the_arg.pa_long_double = va_arg (ap, long double);                  \
+       else                                                                  \
+         the_arg.pa_double = va_arg (ap, double);                            \
+                                                                             \
+       ptr = (const void *) &the_arg;                                        \
+                                                                             \
+       if (fspec == NULL)                                                    \
+         {                                                                   \
+           struct printf_info info = { prec: prec,                           \
+                                       width: width,                         \
+                                       spec: spec,                           \
+                                       is_long_double: is_long_double,       \
+                                       is_short: is_short,                   \
+                                       is_long: is_long,                     \
+                                       alt: alt,                             \
+                                       space: space,                         \
+                                       left: left,                           \
+                                       showsign: showsign,                   \
+                                       group: group,                         \
+                                       pad: pad };                           \
+                                                                             \
+           function_done = __printf_fp (s, &info, &ptr);                     \
+         }                                                                   \
+       else                                                                  \
+         function_done = __printf_fp (s, &fspec->info, &ptr);                \
+                                                                             \
+       if (function_done < 0)                                                \
+         /* Error in print handler.  */                                      \
+         return -1;                                                          \
+                                                                             \
+       done += function_done;                                                \
+      }                                                                              \
+      break;                                                                 \
+                                                                             \
+    LABEL (form_character):                                                  \
+      /* Character.  */                                                              \
+      --width; /* Account for the character itself.  */                      \
+      if (!left)                                                             \
+       PAD (' ');                                                            \
+      outchar ((unsigned char) va_arg (ap, int));      /* Promoted.  */      \
+      if (left)                                                                      \
+       PAD (' ');                                                            \
+      break;                                                                 \
+                                                                             \
+    LABEL (form_string):                                                     \
+      {                                                                              \
+       size_t len;                                                           \
+                                                                             \
+       /* The string argument could in fact be `char *' or `wchar_t *'.      \
+          But this should not make a difference here.  */                    \
+       string = (char *) va_arg (ap, const char *);                          \
+                                                                             \
+       /* Entry point for printing other strings.  */                        \
+      LABEL (print_string):                                                  \
+                                                                             \
+       if (string == NULL)                                                   \
+         {                                                                   \
+           /* Write "(null)" if there's space.  */                           \
+           if (prec == -1 || prec >= (int) sizeof (null) - 1)                \
+             {                                                               \
+               string = (char *) null;                                       \
+               len = sizeof (null) - 1;                                      \
+             }                                                               \
+           else                                                              \
+             {                                                               \
+               string = (char *) "";                                         \
+               len = 0;                                                      \
+             }                                                               \
+         }                                                                   \
+       else if (!is_long)                                                    \
+         {                                                                   \
+           if (prec != -1)                                                   \
+             {                                                               \
+               /* Search for the end of the string, but don't search past    \
+                  the length specified by the precision.  */                 \
+               const char *end = memchr (string, '\0', prec);                \
+               if (end)                                                      \
+                 len = end - string;                                         \
+               else                                                          \
+                 len = prec;                                                 \
+             }                                                               \
+           else                                                              \
+             len = strlen (string);                                          \
+         }                                                                   \
+       else                                                                  \
+         {                                                                   \
+           const wchar_t *s2 = (const wchar_t *) string;                     \
+           mbstate_t mbstate = 0;                                            \
+                                                                             \
+           len = wcsrtombs (NULL, &s2, prec != -1 ? prec : UINT_MAX,         \
+                            &mbstate);                                       \
+           if (len == (size_t) -1)                                           \
+             /* Illegal wide-character string.  */                           \
+             return -1;                                                      \
+                                                                             \
+           s2 = (const wchar_t *) string;                                    \
+           mbstate = 0;                                                      \
+           string = alloca (len + 1);                                        \
+           (void) wcsrtombs (string, &s2, prec != -1 ? prec : UINT_MAX,      \
+                             &mbstate);                                      \
+         }                                                                   \
+                                                                             \
+       if ((width -= len) < 0)                                               \
+         {                                                                   \
+           outstring (string, len);                                          \
+           break;                                                            \
+         }                                                                   \
+                                                                             \
+       if (!left)                                                            \
+         PAD (' ');                                                          \
+       outstring (string, len);                                              \
+       if (left)                                                             \
+         PAD (' ');                                                          \
+      }                                                                              \
+      break;                                                                 \
+                                                                             \
+    LABEL (form_pointer):                                                    \
+      /* Generic pointer.  */                                                \
+      {                                                                              \
+       const void *ptr;                                                      \
+       ptr = va_arg (ap, void *);                                            \
+       if (ptr != NULL)                                                      \
+         {                                                                   \
+           /* If the pointer is not NULL, write it as a %#x spec.  */        \
+           base = 16;                                                        \
+           number.word = (unsigned long int) ptr;                            \
+           is_negative = 0;                                                  \
+           alt = 1;                                                          \
+           group = 0;                                                        \
+           spec = 'x';                                                       \
+           goto LABEL (number);                                              \
+         }                                                                   \
+       else                                                                  \
+         {                                                                   \
+           /* Write "(nil)" for a nil pointer.  */                           \
+           string = (char *) "(nil)";                                        \
+           /* Make sure the full string "(nil)" is printed.  */              \
+           if (prec < 5)                                                     \
+             prec = 5;                                                       \
+           is_long = 0;        /* This is no wide-char string.  */           \
+           goto LABEL (print_string);                                        \
+         }                                                                   \
+      }                                                                              \
+      /* NOTREACHED */                                                       \
+                                                                             \
+    LABEL (form_number):                                                     \
+      /* Answer the count of characters written.  */                         \
+      if (is_longlong)                                                       \
+       *(long long int *) va_arg (ap, void *) = done;                        \
+      else if (is_long)                                                              \
+       *(long int *) va_arg (ap, void *) = done;                             \
+      else if (!is_short)                                                    \
+       *(int *) va_arg (ap, void *) = done;                                  \
+      else                                                                   \
+       *(short int *) va_arg (ap, void *) = done;                            \
+      break;                                                                 \
+                                                                             \
+    LABEL (form_strerror):                                                   \
+      /* Print description of error ERRNO.  */                               \
+      {                                                                              \
+       extern char *_strerror_internal __P ((int, char *buf, size_t));       \
+                                                                             \
+       string = (char *)                                                     \
+         _strerror_internal (errno, work_buffer, sizeof work_buffer);        \
+      }                                                                              \
+      is_long = 0;             /* This is no wide-char string.  */           \
+      goto LABEL (print_string)
+
+
+  /* Sanity check of arguments.  */
   ARGCHECK (s, format);
 
   if (UNBUFFERED_P (s))
@@ -178,101 +790,326 @@ vfprintf (s, format, ap)
        for the stream and then call us again.  */
     return buffered_vfprintf (s, format, ap);
 
-  /* Reset multibyte characters to their initial state.  */
-  (void) mblen ((char *) NULL, 0);
+  /* Initialize local variables.  */
+  done = 0;
+  grouping = (const char *) -1;
+  mbstate = 0;
+  ap_save = ap;
+  nspecs_done = 0;
 
-  /* Figure out the thousands separator character.  */
-  if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
-              strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
-    thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
-  grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
-  if (*grouping == '\0' || *grouping == CHAR_MAX || thousands_sep == L'\0')
-    grouping = NULL;
+  /* Find the first format specifier.  */
+  f = lead_str_end = find_spec (format, &mbstate);
 
-  nspecs_max = 32;             /* A more or less arbitrary start value.  */
-  specs = alloca (nspecs_max * sizeof (struct printf_spec));
-  nspecs = 0;
-  nargs = 0;
-  max_ref_arg = 0;
+  /* Write the literal text before the first format.  */
+  outstring ((const UCHAR_T *) format,
+            lead_str_end - (const UCHAR_T *) format);
 
-  /* Find the first format specifier.  */
-  lead_str_end = find_spec (format);
+  /* If we only have to print a simple string, return now.  */
+  if (*f == L_('\0'))
+    return done;
 
-  for (f = lead_str_end; *f != '\0'; f = specs[nspecs++].next_fmt)
+  /* Process whole format string.  */
+  do
     {
-      if (nspecs >= nspecs_max)
+#define REF(Name) &&do_##Name
+#define LABEL(Name) do_##Name
+      STEP0_3_TABLE;
+      STEP4_TABLE;
+
+      int is_negative; /* Flag for negative number.  */
+      union
+      {
+       unsigned long long int longlong;
+       unsigned long int word;
+      } number;
+      int base;
+      union printf_arg the_arg;
+      char *string;    /* Pointer to argument string.  */
+      int alt = 0;     /* Alternate format.  */
+      int space = 0;   /* Use space prefix if no sign is needed.  */
+      int left = 0;    /* Left-justify output.  */
+      int showsign = 0;        /* Always begin with plus or minus sign.  */
+      int group = 0;   /* Print numbers according grouping rules.  */
+      int is_long_double = 0; /* Argument is long double/ long long int.  */
+      int is_short = 0;        /* Argument is long int.  */
+      int is_long = 0; /* Argument is short int.  */
+      int width = 0;   /* Width of output; 0 means none specified.  */
+      int prec = -1;   /* Precision of output; -1 means none specified.  */
+      char pad = ' ';  /* Padding character.  */
+      CHAR_T spec;
+
+      /* Get current character in format string.  */
+      JUMP (*++f, step0_jumps);
+
+      /* ' ' flag.  */
+    LABEL (flag_space):
+      space = 1;
+      JUMP (*++f, step0_jumps);
+
+      /* '+' flag.  */
+    LABEL (flag_plus):
+      showsign = 1;
+      JUMP (*++f, step0_jumps);
+
+      /* The '-' flag.  */
+    LABEL (flag_minus):
+      left = 1;
+      pad = L_(' ');
+      JUMP (*++f, step0_jumps);
+
+      /* The '#' flag.  */
+    LABEL (flag_hash):
+      alt = 1;
+      JUMP (*++f, step0_jumps);
+
+      /* The '0' flag.  */
+    LABEL (flag_zero):
+      if (!left)
+       pad = L_('0');
+      JUMP (*++f, step0_jumps);
+
+      /* The '\'' flag.  */
+    LABEL (flag_quote):
+      group = 1;
+
+      /* XXX Completely wrong.  Use wctob.  */
+      if (grouping == (const char *) -1)
        {
-         /* Extend the array of format specifiers.  */
-         struct printf_spec *old = specs;
-
-         nspecs_max *= 2;
-         specs = alloca (nspecs_max * sizeof (struct printf_spec));
-         if (specs == &old[nspecs])
-           /* Stack grows up, OLD was the last thing allocated; extend it.  */
-           nspecs_max += nspecs_max / 2;
-         else
-           {
-             /* Copy the old array's elements to the new space.  */
-             memcpy (specs, old, nspecs * sizeof (struct printf_spec));
-             if (old == &specs[nspecs])
-               /* Stack grows down, OLD was just below the new SPECS.
-                  We can use that space when the new space runs out.  */
-               nspecs_max += nspecs_max / 2;
-           }
+         /* Figure out the thousands separator character.  */
+         if (mbtowc (&thousands_sep,
+                     _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
+                     strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
+           thousands_sep = (wchar_t)
+             *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
+         grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
+         if (*grouping == '\0' || *grouping == CHAR_MAX
+             || thousands_sep == L'\0')
+           grouping = NULL;
        }
+      JUMP (*++f, step0_jumps);
 
-      /* Parse the format specifier.  */
-      nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg);
-    }
+      /* Get width from argument.  */
+    LABEL (width_asterics):
+      {
+       const UCHAR_T *tmp;     /* Temporary value.  */
 
-  /* Determine the number of arguments the format string consumes.  */
-  nargs = MAX (nargs, max_ref_arg);
+       tmp = ++f;
+       if (ISDIGIT (*tmp) && read_int (&tmp) && *tmp == L_('$'))
+         /* The width comes from an positional parameter.  */
+         goto do_positional;
 
-  /* Allocate memory for the argument descriptions.  */
-  args_type = alloca (nargs * sizeof (int));
-  memset (args_type, 0, nargs * sizeof (int));
-  args_value = alloca (nargs * sizeof (union printf_arg));
+       width = va_arg (ap, int);
 
-  /* XXX Could do sanity check here: If any element in ARGS_TYPE is
-     still zero after this loop, format is invalid.  For now we simply
-     use 0 as the value.  */
+       /* Negative width means left justified.  */
+       if (width < 0)
+         {
+           width = -width;
+           pad = L_(' ');
+           left = 1;
+         }
+      }
+      JUMP (*f, step1_jumps);
+
+      /* Given width in format string.  */
+    LABEL (width):
+      width = read_int (&f);
+      if (*f == L_('$'))
+       /* Oh, oh.  The argument comes from an positional parameter.  */
+       goto do_positional;
+      JUMP (*f, step1_jumps);
+
+    LABEL (precision):
+      ++f;
+      if (*f == L_('*'))
+       {
+         const UCHAR_T *tmp;   /* Temporary value.  */
 
-  /* Fill in the types of all the arguments.  */
-  for (cnt = 0; cnt < nspecs; ++cnt)
-    {
-      /* If the width is determined by an argument this is an int.  */
-      if (specs[cnt].width_arg != -1)
-       args_type[specs[cnt].width_arg] = PA_INT;
+         tmp = ++f;
+         if (ISDIGIT (*tmp) && read_int (&tmp) > 0 && *tmp == L_('$'))
+           /* The precision comes from an positional parameter.  */
+           goto do_positional;
 
-      /* If the precision is determined by an argument this is an int.  */
-      if (specs[cnt].prec_arg != -1)
-       args_type[specs[cnt].prec_arg] = PA_INT;
+         prec = va_arg (ap, int);
 
-      switch (specs[cnt].ndata_args)
+         /* If the precision is negative the precision is omitted.  */
+         if (prec < 0)
+           prec = -1;
+       }
+      else if (ISDIGIT (*f))
+       prec = read_int (&f);
+      else
+       prec = 0;
+      JUMP (*f, step2_jumps);
+
+      /* Process 'h' modifier.  No other modifier is allowed to
+        follow.  */
+    LABEL (mod_half):
+      is_short = 1;
+      JUMP (*++f, step4_jumps);
+
+      /* Process 'l' modifier.  There might another 'l' follow.  */
+    LABEL (mod_long):
+      is_long = 1;
+      JUMP (*++f, step3_jumps);
+
+      /* Process 'L', 'q', or 'll' modifier.  No other modifier is
+        allowed to follow.  */
+    LABEL (mod_longlong):
+      is_long_double = 1;
+      JUMP (*++f, step4_jumps);
+
+    LABEL (mod_size_t):
+      is_longlong = sizeof (size_t) > sizeof (unsigned long int);
+      is_long = sizeof (size_t) > sizeof (unsigned int);
+      JUMP (*++f, step4_jumps);
+
+
+      /* Process current format.  */
+      while (1)
        {
-       case 0:                 /* No arguments.  */
-         break;
-       case 1:                 /* One argument; we already have the type.  */
-         args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type;
-         break;
-       default:
-         /* We have more than one argument for this format spec.  We must
-            call the arginfo function again to determine all the types.  */
-         (void) (*__printf_arginfo_table[specs[cnt].info.spec])
-           (&specs[cnt].info,
-            specs[cnt].ndata_args, &args_type[specs[cnt].data_arg]);
-         break;
+         process_arg (((struct printf_spec *) NULL));
+
+       LABEL (form_unknown):
+         if (spec == L_('\0'))
+           /* The format string ended before the specifier is complete.  */
+           return -1;
+
+         /* If we are in the fast loop force entering the complicated
+            one.  */
+         goto do_positional;
        }
+
+      /* Look for next format specifier.  */
+      f = find_spec ((end_of_spec = ++f), &mbstate);
+
+      /* Write the following constant string.  */
+      outstring (end_of_spec, f - end_of_spec);
     }
+  while (*f != L_('\0'));
 
-  /* Now we know all the types and the order.  Fill in the argument values.  */
-  for (cnt = 0; cnt < nargs; ++cnt)
-    switch (args_type[cnt])
+  /* We processed the whole format without any positional parameters.  */
+  return done;
+
+  /* Here starts the more complex loop to handle positional parameters.  */
+do_positional:
+  {
+    /* Array with information about the needed arguments.  This has to
+       be dynamically extendable.  */
+    size_t nspecs = 0;
+    size_t nspecs_max = 32;    /* A more or less arbitrary start value.  */
+    struct printf_spec *specs
+      = alloca (nspecs_max * sizeof (struct printf_spec));
+
+    /* The number of arguments the format string requests.  This will
+       determine the size of the array needed to store the argument
+       attributes.  */
+    size_t nargs = 0;
+    int *args_type;
+    union printf_arg *args_value;
+
+    /* Positional parameters refer to arguments directly.  This could
+       also determine the maximum number of arguments.  Track the
+       maximum number.  */
+    size_t max_ref_arg = 0;
+
+    /* Just a counter.  */
+    int cnt;
+
+
+    if (grouping == (const char *) -1)
       {
+       /* XXX Use wctob.  But this is incompatible for now.  */
+       /* Figure out the thousands separator character.  */
+       if (mbtowc (&thousands_sep,
+                   _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
+                   strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
+         thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
+       grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
+       if (*grouping == '\0' || *grouping == CHAR_MAX
+           || thousands_sep == L'\0')
+         grouping = NULL;
+      }
+
+    for (f = lead_str_end; *f != '\0'; f = specs[nspecs++].next_fmt)
+      {
+       if (nspecs >= nspecs_max)
+         {
+           /* Extend the array of format specifiers.  */
+           struct printf_spec *old = specs;
+
+           nspecs_max *= 2;
+           specs = alloca (nspecs_max * sizeof (struct printf_spec));
+
+           if (specs == &old[nspecs])
+             /* Stack grows up, OLD was the last thing allocated;
+                extend it.  */
+             nspecs_max += nspecs_max / 2;
+           else
+             {
+               /* Copy the old array's elements to the new space.  */
+               memcpy (specs, old, nspecs * sizeof (struct printf_spec));
+               if (old == &specs[nspecs])
+                 /* Stack grows down, OLD was just below the new
+                    SPECS.  We can use that space when the new space
+                    runs out.  */
+                 nspecs_max += nspecs_max / 2;
+             }
+         }
+
+       /* Parse the format specifier.  */
+       nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg, NULL);
+      }
+
+    /* Determine the number of arguments the format string consumes.  */
+    nargs = MAX (nargs, max_ref_arg);
+
+    /* Allocate memory for the argument descriptions.  */
+    args_type = alloca (nargs * sizeof (int));
+    memset (args_type, 0, nargs * sizeof (int));
+    args_value = alloca (nargs * sizeof (union printf_arg));
+
+    /* XXX Could do sanity check here: If any element in ARGS_TYPE is
+       still zero after this loop, format is invalid.  For now we
+       simply use 0 as the value.  */
+
+    /* Fill in the types of all the arguments.  */
+    for (cnt = 0; cnt < nspecs; ++cnt)
+      {
+       /* If the width is determined by an argument this is an int.  */
+       if (specs[cnt].width_arg != -1)
+         args_type[specs[cnt].width_arg] = PA_INT;
+
+       /* If the precision is determined by an argument this is an int.  */
+       if (specs[cnt].prec_arg != -1)
+         args_type[specs[cnt].prec_arg] = PA_INT;
+
+       switch (specs[cnt].ndata_args)
+         {
+         case 0:               /* No arguments.  */
+           break;
+         case 1:               /* One argument; we already have the type.  */
+           args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type;
+           break;
+         default:
+           /* We have more than one argument for this format spec.
+              We must call the arginfo function again to determine
+              all the types.  */
+           (void) (*__printf_arginfo_table[specs[cnt].info.spec])
+             (&specs[cnt].info,
+              specs[cnt].ndata_args, &args_type[specs[cnt].data_arg]);
+           break;
+         }
+      }
+
+    /* Now we know all the types and the order.  Fill in the argument
+       values.  */
+    for (cnt = 0, ap = ap_save; cnt < nargs; ++cnt)
+      switch (args_type[cnt])
+       {
 #define T(tag, mem, type)                                                    \
-      case tag:                                                                      \
-       args_value[cnt].mem = va_arg (ap, type);                              \
-       break
+       case tag:                                                             \
+         args_value[cnt].mem = va_arg (ap, type);                            \
+         break
 
        T (PA_CHAR, pa_char, int); /* Promoted.  */
        T (PA_INT|PA_FLAG_SHORT, pa_short_int, int); /* Promoted.  */
@@ -285,349 +1122,126 @@ vfprintf (s, format, ap)
        T (PA_STRING, pa_string, const char *);
        T (PA_POINTER, pa_pointer, void *);
 #undef T
-      default:
-       if ((args_type[cnt] & PA_FLAG_PTR) != 0)
-         args_value[cnt].pa_pointer = va_arg (ap, void *);
-       else
-         args_value[cnt].pa_long_double = 0.0;
-       break;
-      }
-
-  /* Write the literal text before the first format.  */
-  outstring (format, lead_str_end - format);
+       default:
+         if ((args_type[cnt] & PA_FLAG_PTR) != 0)
+           args_value[cnt].pa_pointer = va_arg (ap, void *);
+         else
+           args_value[cnt].pa_long_double = 0.0;
+         break;
+       }
 
-  /* Now walk through all format specifiers and process them.  */
-  for (cnt = 0; cnt < nspecs; ++cnt)
-    {
-      printf_function *function; /* Auxiliary function to do output.  */
-      int is_neg;              /* Decimal integer is negative.  */
-      int base;                        /* Base of a number to be written.  */
-      unsigned long long int num; /* Integral number to be written.  */
-      const char *str;         /* String to be written.  */
-      char errorbuf[1024];      /* Buffer sometimes used by %m.  */
-
-      if (specs[cnt].width_arg != -1)
+    /* Now walk through all format specifiers and process them.  */
+    for (; nspecs_done < nspecs; ++nspecs_done)
+      {
+#undef REF
+#define REF(Name) &&do2_##Name
+#undef LABEL
+#define LABEL(Name) do2_##Name
+       STEP4_TABLE;
+
+       int is_negative;
+       union
        {
-         /* Extract the field width from an argument.  */
-         specs[cnt].info.width = args_value[specs[cnt].width_arg].pa_int;
+         unsigned long long int longlong;
+         unsigned long int word;
+       } number;
+       int base;
+       union printf_arg the_arg;
+       char *string;   /* Pointer to argument string.  */
+
+       /* Fill variables from values in struct.  */
+       int alt = specs[nspecs_done].info.alt;
+       int space = specs[nspecs_done].info.space;
+       int left = specs[nspecs_done].info.left;
+       int showsign = specs[nspecs_done].info.showsign;
+       int group = specs[nspecs_done].info.group;
+       int is_long_double = specs[nspecs_done].info.is_long_double;
+       int is_short = specs[nspecs_done].info.is_short;
+       int is_long = specs[nspecs_done].info.is_long;
+       int width = specs[nspecs_done].info.width;
+       int prec = specs[nspecs_done].info.prec;
+       char pad = specs[nspecs_done].info.pad;
+       CHAR_T spec = specs[nspecs_done].info.spec;
+
+       /* Fill in last information.  */
+       if (specs[nspecs_done].width_arg != -1)
+         {
+           /* Extract the field width from an argument.  */
+           specs[nspecs_done].info.width =
+             args_value[specs[nspecs_done].width_arg].pa_int;
+
+           if (specs[nspecs_done].info.width < 0)
+             /* If the width value is negative left justification is
+                selected and the value is taken as being positive.  */
+             {
+               specs[nspecs_done].info.width *= -1;
+               left = specs[nspecs_done].info.left = 1;
+             }
+           width = specs[nspecs_done].info.width;
+         }
 
-         if (specs[cnt].info.width < 0)
-           /* If the width value is negative left justification is selected
-              and the value is taken as being positive.  */
-           {
-             specs[cnt].info.width = -specs[cnt].info.width;
-             specs[cnt].info.left = 1;
-           }
-       }
+       if (specs[nspecs_done].prec_arg != -1)
+         {
+           /* Extract the precision from an argument.  */
+           specs[nspecs_done].info.prec =
+             args_value[specs[nspecs_done].prec_arg].pa_int;
 
-      if (specs[cnt].prec_arg != -1)
-       {
-         /* Extract the precision from an argument.  */
-         specs[cnt].info.prec = args_value[specs[cnt].prec_arg].pa_int;
+           if (specs[nspecs_done].info.prec < 0)
+             /* If the precision is negative the precision is
+                omitted.  */
+             specs[nspecs_done].info.prec = -1;
 
-         if (specs[cnt].info.prec < 0)
-           /* If the precision is negative the precision is omitted.  */
-           specs[cnt].info.prec = -1;
-       }
+           prec = specs[nspecs_done].info.prec;
+         }
 
-      /* Check for a user-defined handler for this spec.  */
-      function = (__printf_function_table == NULL ? NULL :
-                  __printf_function_table[specs[cnt].info.spec]);
+       /* Process format specifiers.  */
+       while (1)
+         {
+           JUMP (spec, step4_jumps);
 
-      if (function != NULL)
-      use_function:            /* Built-in formats with helpers use this.  */
-       {
-         int function_done;
-         unsigned int i;
-         const void *ptr[specs[cnt].ndata_args];
+           process_arg ((&specs[nspecs_done]));
 
-         /* Fill in an array of pointers to the argument values.  */
-         for (i = 0; i < specs[cnt].ndata_args; ++i)
-           ptr[i] = &args_value[specs[cnt].data_arg + i];
+         LABEL (form_unknown):
+           {
+             extern printf_function **__printf_function_table;
+             int function_done;
+             printf_function *function;
+             unsigned int i;
+             const void **ptr;
 
-         /* Call the function.  */
-         function_done = (*function) (s, &specs[cnt].info, ptr);
+             function =
+               (__printf_function_table == NULL ? NULL :
+                __printf_function_table[specs[nspecs_done].info.spec]);
 
-         /* If an error occured don't do any further work.  */
-         if (function_done < 0)
-           return -1;
+             if (function == NULL)
+               function = &printf_unknown;
 
-         done += function_done;
-       }
-      else
-       switch (specs[cnt].info.spec)
-         {
-         case '%':
-           /* Write a literal "%".  */
-           outchar ('%');
-           break;
-         case 'i':
-         case 'd':
-           {
-             long long int signed_num;
-
-             /* Decimal integer.  */
-             base = 10;
-             if (specs[cnt].info.is_longlong)
-               signed_num = args_value[specs[cnt].data_arg].pa_long_long_int;
-             else if (specs[cnt].info.is_long)
-               signed_num = args_value[specs[cnt].data_arg].pa_long_int;
-             else if (!specs[cnt].info.is_short)
-               signed_num = args_value[specs[cnt].data_arg].pa_int;
-             else
-               signed_num = args_value[specs[cnt].data_arg].pa_short_int;
-
-             is_neg = signed_num < 0;
-             num = is_neg ? (- signed_num) : signed_num;
-             goto number;
-           }
+             ptr = alloca (specs[nspecs_done].ndata_args
+                           * sizeof (const void *));
+
+             /* Fill in an array of pointers to the argument values.  */
+             for (i = 0; i < specs[nspecs_done].ndata_args; ++i)
+               ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
 
-         case 'u':
-           /* Decimal unsigned integer.  */
-            base = 10;
-            goto unsigned_number;
-
-         case 'o':
-            /* Octal unsigned integer.  */
-            base = 8;
-            goto unsigned_number;
-
-          case 'X':
-            /* Hexadecimal unsigned integer.  */
-          case 'x':
-            /* Hex with lower-case digits.  */
-            base = 16;
-
-         unsigned_number:
-            /* Unsigned number of base BASE.  */
-
-            if (specs[cnt].info.is_longlong)
-             num = args_value[specs[cnt].data_arg].pa_u_long_long_int;
-            else if (specs[cnt].info.is_long)
-             num = args_value[specs[cnt].data_arg].pa_u_long_int;
-            else if (!specs[cnt].info.is_short)
-             num = args_value[specs[cnt].data_arg].pa_u_int;
-            else
-             num = args_value[specs[cnt].data_arg].pa_u_short_int;
-
-            /* ANSI only specifies the `+' and
-               ` ' flags for signed conversions.  */
-            is_neg = 0;
-           specs[cnt].info.showsign = 0;
-           specs[cnt].info.space = 0;
-
-         number:
-           /* Number of base BASE.  */
-            {
-              char work[BUFSIZ];
-              char *const workend = &work[sizeof(work) - 1];
-              register char *w;
-
-              if (specs[cnt].info.prec == -1)
-                 /* Supply a default precision if none was given.  */
-                 specs[cnt].info.prec = 1;
-             else
-               /* We have to take care for the '0' flag.  If a
-                  precision is given it must be ignored.  */
-               specs[cnt].info.pad = ' ';
-
-             /* If the precision is 0 and the number is 0 nothing has
-                to be written for the number.  */
-             if (specs[cnt].info.prec == 0 && num == 0)
-               w = workend;
-             else
-               {
-                 /* Put the number in WORK.  */
-                 w = _itoa (num, workend + 1, base,
-                            specs[cnt].info.spec == 'X');
-                 w -= 1;
-                 if (specs[cnt].info.group && grouping)
-                   w = group_number (w, workend, grouping, thousands_sep);
-               }
-              specs[cnt].info.width -= workend - w;
-              specs[cnt].info.prec -= workend - w;
-
-              if (num != 0 && specs[cnt].info.alt && base == 8
-                 && specs[cnt].info.prec <= 0)
-                {
-                 /* Add octal marker.  */
-                  *w-- = '0';
-                  --specs[cnt].info.width;
-                }
-
-              if (specs[cnt].info.prec > 0)
-                {
-                 /* Add zeros to the precision.  */
-                  specs[cnt].info.width -= specs[cnt].info.prec;
-                  while (specs[cnt].info.prec-- > 0)
-                    *w-- = '0';
-                }
-
-              if (num != 0 && specs[cnt].info.alt && base == 16)
-               /* Account for 0X hex marker.  */
-                specs[cnt].info.width -= 2;
-
-              if (is_neg || specs[cnt].info.showsign || specs[cnt].info.space)
-                --specs[cnt].info.width;
-
-              if (!specs[cnt].info.left && specs[cnt].info.pad == ' ')
-                PAD (' ');
-
-              if (is_neg)
-                outchar ('-');
-              else if (specs[cnt].info.showsign)
-                outchar ('+');
-              else if (specs[cnt].info.space)
-                outchar (' ');
-
-              if (num != 0 && specs[cnt].info.alt && base == 16)
-                {
-                  outchar ('0');
-                  outchar (specs[cnt].info.spec);
-                }
-
-              if (!specs[cnt].info.left && specs[cnt].info.pad == '0')
-                PAD ('0');
-
-              /* Write the number.  */
-              while (++w <= workend)
-                outchar (*w);
-
-              if (specs[cnt].info.left)
-                PAD (' ');
-            }
-            break;
-
-          case 'e':
-          case 'E':
-          case 'f':
-          case 'g':
-          case 'G':
-            {
-              /* Floating-point number.  This is handled by printf_fp.c.  */
-              extern printf_function __printf_fp;
-              function = __printf_fp;
-              goto use_function;
-            }
-
-          case 'c':
-            /* Character.  */
-           --specs[cnt].info.width;/* Account for the character itself.  */
-            if (!specs[cnt].info.left)
-             PAD (' ');
-            outchar ((unsigned char) args_value[specs[cnt].data_arg].pa_char);
-            if (specs[cnt].info.left)
-              PAD (' ');
-            break;
-
-          case 's':
-            {
-              static const char null[] = "(null)";
-              size_t len;
-
-             str = args_value[specs[cnt].data_arg].pa_string;
-
-           string:
-
-              if (str == NULL)
-               {
-                 /* Write "(null)" if there's space.  */
-                 if (specs[cnt].info.prec == -1
-                     || specs[cnt].info.prec >= (int) sizeof (null) - 1)
-                   {
-                     str = null;
-                     len = sizeof (null) - 1;
-                   }
-                 else
-                   {
-                     str = "";
-                     len = 0;
-                   }
-               }
-              else if (specs[cnt].info.prec != -1)
-               {
-                 /* Search for the end of the string, but don't search
-                    past the length specified by the precision.  */
-                 const char *end = memchr (str, '\0', specs[cnt].info.prec);
-                 if (end)
-                   len = end - str;
-                 else
-                   len = specs[cnt].info.prec;
-               }
-             else
-               len = strlen (str);
-
-              specs[cnt].info.width -= len;
-
-              if (!specs[cnt].info.left)
-                PAD (' ');
-              outstring (str, len);
-              if (specs[cnt].info.left)
-                PAD (' ');
-            }
-            break;
-
-          case 'p':
-            /* Generic pointer.  */
-            {
-              const void *ptr;
-              ptr = args_value[specs[cnt].data_arg].pa_pointer;
-              if (ptr != NULL)
-                {
-                  /* If the pointer is not NULL, write it as a %#x spec.  */
-                  base = 16;
-                  num = (unsigned long long int) (unsigned long int) ptr;
-                  is_neg = 0;
-                  specs[cnt].info.alt = 1;
-                 specs[cnt].info.spec = 'x';
-                  specs[cnt].info.group = 0;
-                  goto number;
-                }
-              else
-                {
-                  /* Write "(nil)" for a nil pointer.  */
-                  str = "(nil)";
-                 /* Make sure the full string "(nil)" is printed.  */
-                 if (specs[cnt].info.prec < 5)
-                   specs[cnt].info.prec = 5;
-                  goto string;
-                }
-            }
-            break;
-
-          case 'n':
-            /* Answer the count of characters written.  */
-            if (specs[cnt].info.is_longlong)
-             *(long long int *)
-               args_value[specs[cnt].data_arg].pa_pointer = done;
-            else if (specs[cnt].info.is_long)
-             *(long int *)
-               args_value[specs[cnt].data_arg].pa_pointer = done;
-            else if (!specs[cnt].info.is_short)
-             *(int *)
-               args_value[specs[cnt].data_arg].pa_pointer = done;
-            else
-             *(short int *)
-               args_value[specs[cnt].data_arg].pa_pointer = done;
-            break;
-
-          case 'm':
-            {
-              extern char *_strerror_internal __P ((int, char *buf, size_t));
-              str = _strerror_internal (errno, errorbuf, sizeof errorbuf);
-              goto string;
-            }
-
-          default:
-            /* Unrecognized format specifier.  */
-            function = printf_unknown;
-            goto use_function;
+             /* Call the function.  */
+             function_done = (*function) (s, &specs[nspecs_done].info, ptr);
+
+             /* If an error occured we don't have information about #
+                of chars.  */
+             if (function_done < 0)
+               return -1;
+
+             done += function_done;
+           }
+           break;
          }
 
-      /* Write the following constant string.  */
-      outstring (specs[cnt].end_of_fmt,
-                specs[cnt].next_fmt - specs[cnt].end_of_fmt);
-    }
+       /* Write the following constant string.  */
+       outstring (specs[nspecs_done].end_of_fmt,
+                  specs[nspecs_done].next_fmt
+                  - specs[nspecs_done].end_of_fmt);
+      }
+  }
 
   return done;
 }
@@ -636,7 +1250,7 @@ vfprintf (s, format, ap)
 # undef vfprintf
 # ifdef strong_alias
 /* This is for glibc.  */
-strong_alias (_IO_vfprintf, vfprintf)
+strong_alias (_IO_vfprintf, vfprintf);
 # else
 #  if defined __ELF__ || defined __GNU_LIBRARY__
 #   include <gnu-stabs.h>
@@ -646,20 +1260,16 @@ weak_alias (_IO_vfprintf, vfprintf);
 #  endif
 # endif
 #endif
-
-
+\f
 /* Handle an unknown format specifier.  This prints out a canonicalized
    representation of the format spec itself.  */
-
 static int
-printf_unknown (s, info, args)
-  FILE *s;
-  const struct printf_info *info;
-  const void *const *args;
+printf_unknown (FILE *s, const struct printf_info *info,
+               const void *const *args)
+
 {
   int done = 0;
-  char work[BUFSIZ];
-  char *const workend = &work[sizeof(work) - 1];
+  char work_buffer[BUFSIZ];
   register char *w;
 
   outchar ('%');
@@ -679,7 +1289,7 @@ printf_unknown (s, info, args)
 
   if (info->width != 0)
     {
-      w = _itoa (info->width, workend + 1, 10, 0);
+      w = _itoa_word (info->width, workend + 1, 10, 0);
       while (++w <= workend)
        outchar (*w);
     }
@@ -687,7 +1297,7 @@ printf_unknown (s, info, args)
   if (info->prec != -1)
     {
       outchar ('.');
-      w = _itoa (info->prec, workend + 1, 10, 0);
+      w = _itoa_word (info->prec, workend + 1, 10, 0);
       while (++w <= workend)
        outchar (*w);
     }
@@ -700,9 +1310,8 @@ printf_unknown (s, info, args)
 \f
 /* Group the digits according to the grouping rules of the current locale.
    The interpretation of GROUPING is as in `struct lconv' from <locale.h>.  */
-
 static char *
-group_number (char *w, char *workend, const char *grouping,
+group_number (CHAR_T *w, CHAR_T *rear_ptr, const CHAR_T *grouping,
              wchar_t thousands_sep)
 {
   int len;
@@ -717,10 +1326,10 @@ group_number (char *w, char *workend, const char *grouping,
   len = *grouping;
 
   /* Copy existing string so that nothing gets overwritten.  */
-  src = (char *) alloca (workend - w);
-  memcpy (src, w + 1, workend - w);
-  s = &src[workend - w - 1];
-  w = workend;
+  src = (char *) alloca (rear_ptr - w);
+  memcpy (src, w + 1, rear_ptr - w);
+  s = &src[rear_ptr - w - 1];
+  w = rear_ptr;
 
   /* Process all characters in the string.  */
   while (s >= src)
@@ -772,32 +1381,30 @@ _IO_helper_overflow (_IO_FILE *s, int c)
 }
 
 static const struct _IO_jump_t _IO_helper_jumps =
-  {
-    JUMP_INIT_DUMMY,
-    JUMP_INIT (finish, _IO_default_finish),
-    JUMP_INIT (overflow, _IO_helper_overflow),
-    JUMP_INIT (underflow, _IO_default_underflow),
-    JUMP_INIT (uflow, _IO_default_uflow),
-    JUMP_INIT (pbackfail, _IO_default_pbackfail),
-    JUMP_INIT (xsputn, _IO_default_xsputn),
-    JUMP_INIT (xsgetn, _IO_default_xsgetn),
-    JUMP_INIT (seekoff, _IO_default_seekoff),
-    JUMP_INIT (seekpos, _IO_default_seekpos),
-    JUMP_INIT (setbuf, _IO_default_setbuf),
-    JUMP_INIT (sync, _IO_default_sync),
-    JUMP_INIT (doallocate, _IO_default_doallocate),
-    JUMP_INIT (read, _IO_default_read),
-    JUMP_INIT (write, _IO_default_write),
-    JUMP_INIT (seek, _IO_default_seek),
-    JUMP_INIT (close, _IO_default_close),
-    JUMP_INIT (stat, _IO_default_stat)
-  };
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT (finish, _IO_default_finish),
+  JUMP_INIT (overflow, _IO_helper_overflow),
+  JUMP_INIT (underflow, _IO_default_underflow),
+  JUMP_INIT (uflow, _IO_default_uflow),
+  JUMP_INIT (pbackfail, _IO_default_pbackfail),
+  JUMP_INIT (xsputn, _IO_default_xsputn),
+  JUMP_INIT (xsgetn, _IO_default_xsgetn),
+  JUMP_INIT (seekoff, _IO_default_seekoff),
+  JUMP_INIT (seekpos, _IO_default_seekpos),
+  JUMP_INIT (setbuf, _IO_default_setbuf),
+  JUMP_INIT (sync, _IO_default_sync),
+  JUMP_INIT (doallocate, _IO_default_doallocate),
+  JUMP_INIT (read, _IO_default_read),
+  JUMP_INIT (write, _IO_default_write),
+  JUMP_INIT (seek, _IO_default_seek),
+  JUMP_INIT (close, _IO_default_close),
+  JUMP_INIT (stat, _IO_default_stat)
+};
 
 static int
-buffered_vfprintf (s, format, args)
-  register _IO_FILE *s;
-  char const *format;
-  _IO_va_list args;
+buffered_vfprintf (register _IO_FILE *s, const CHAR_T *format,
+                  _IO_va_list args)
 {
   char buf[_IO_BUFSIZ];
   struct helper_file helper;
@@ -828,10 +1435,7 @@ buffered_vfprintf (s, format, args)
 #else /* !USE_IN_LIBIO */
 
 static int
-buffered_vfprintf (s, format, args)
-  register FILE *s;
-  char const *format;
-  va_list args;
+buffered_vfprintf (register FILE *s, const CHAR_T *format, va_list args)
 {
   char buf[BUFSIZ];
   int result;
@@ -851,26 +1455,28 @@ buffered_vfprintf (s, format, args)
 
   return result;
 }
-
-
+\f
 /* Pads string with given number of a specified character.
    This code is taken from iopadn.c of the GNU I/O library.  */
 #define PADSIZE 16
-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'};
+static const CHAR_T blanks[PADSIZE] =
+{ L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '),
+  L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' ') };
+static const CHAR_T zeroes[PADSIZE] =
+{ L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'),
+  L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0') };
 
 ssize_t
-__printf_pad (s, pad, count)
-     FILE *s;
-     char pad;
-     size_t count;
+#ifndef COMPILE_WPRINTF
+__printf_pad (FILE *s, char pad, size_t count)
+#else
+__wprintf_pad (FILE *s, wchar_t pad, size_t count)
+#endif
 {
-  const char *padptr;
+  const CHAR_T *padptr;
   register size_t i;
 
-  padptr = pad == ' ' ? blanks : zeroes;
+  padptr = pad == L_(' ') ? blanks : zeroes;
 
   for (i = count; i >= PADSIZE; i -= PADSIZE)
     if (PUT (s, padptr, PADSIZE) != PADSIZE)