about summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>1996-04-03 16:31:49 +0000
committerRoland McGrath <roland@gnu.org>1996-04-03 16:31:49 +0000
commit299a95b9f048679f1288512b0a6ab6ca16dd9d7c (patch)
tree4788a62ee8f131887e5817e8daa3339084dbe764 /stdio-common
parent30de3b18a526acc837957865129b7fa6d8ac91d1 (diff)
downloadglibc-299a95b9f048679f1288512b0a6ab6ca16dd9d7c.tar.gz
glibc-299a95b9f048679f1288512b0a6ab6ca16dd9d7c.tar.xz
glibc-299a95b9f048679f1288512b0a6ab6ca16dd9d7c.zip
Tue Apr 2 21:27:01 1996 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
	* posix/glob.c (glob_pattern_p): Avoid scanning past eos if
	the pattern ends with a backslash and quoting is enabled.
	* posix/fnmatch.c (fnmatch): Likewise; return FNM_NOMATCH for such
 	patterns.
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/_itoa.c6
-rw-r--r--stdio-common/_itoa.h29
-rw-r--r--stdio-common/printf-parse.h135
-rw-r--r--stdio-common/printf-prs.c52
-rw-r--r--stdio-common/printf.h9
-rw-r--r--stdio-common/tst-printf.c17
-rw-r--r--stdio-common/vfprintf.c1710
7 files changed, 1327 insertions, 631 deletions
diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c
index caa8179624..38d5367ba2 100644
--- a/stdio-common/_itoa.c
+++ b/stdio-common/_itoa.c
@@ -1,5 +1,5 @@
 /* Internal function for converting integers to ASCII.
-Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
 Contributed by Torbjorn Granlund <tege@matematik.su.se>
 and Ulrich Drepper <drepper@gnu.ai.mit.edu>.
@@ -159,10 +159,10 @@ static const struct base_table_t base_table[] =
 };
 
 /* Lower-case digits.  */
-static const char _itoa_lower_digits[]
+const char _itoa_lower_digits[]
 	= "0123456789abcdefghijklmnopqrstuvwxyz";
 /* Upper-case digits.  */
-static const char _itoa_upper_digits[]
+const char _itoa_upper_digits[]
 	= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
 
diff --git a/stdio-common/_itoa.h b/stdio-common/_itoa.h
index ab3d1d1d3a..75f5f85892 100644
--- a/stdio-common/_itoa.h
+++ b/stdio-common/_itoa.h
@@ -1,5 +1,5 @@
 /* Internal function for converting integers to ASCII.
-Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
 
 The GNU C Library is free software; you can redistribute it and/or
@@ -29,4 +29,31 @@ Cambridge, MA 02139, USA.  */
 extern char *_itoa __P ((unsigned long long int value, char *buflim,
 			 unsigned int base, int upper_case));
 
+static inline char *_itoa_word (unsigned long value, char *buflim,
+				unsigned int base, int upper_case)
+{
+  extern const char _itoa_upper_digits[], _itoa_lower_digits[];
+  const char *digits = upper_case ? _itoa_upper_digits : _itoa_lower_digits;
+  char *bp = buflim;
+
+  switch (base)
+    {
+#define SPECIAL(Base)							      \
+    case Base:								      \
+      do								      \
+	*--bp = digits[value % Base];					      \
+      while ((value /= Base) != 0);					      \
+      break
+
+      SPECIAL (10);
+      SPECIAL (16);
+      SPECIAL (8);
+    default:
+      do
+	*--bp = digits[value % base];
+      while ((value /= base) != 0);
+    }
+  return bp;
+}
+
 #endif	/* itoa.h */
diff --git a/stdio-common/printf-parse.h b/stdio-common/printf-parse.h
index 9abbdba187..a7960e6003 100644
--- a/stdio-common/printf-parse.h
+++ b/stdio-common/printf-parse.h
@@ -1,5 +1,5 @@
 /* Internal header for parsing printf format strings.
-Copyright (C) 1995 Free Software Foundation, Inc.
+Copyright (C) 1995, 1996 Free Software Foundation, Inc.
 This file is part of th GNU C Library.
 
 The GNU C Library is free software; you can redistribute it and/or
@@ -73,14 +73,14 @@ union printf_arg
 /* Read a simple integer from a string and update the string pointer.
    It is assumed that the first character is a digit.  */
 static inline unsigned int
-read_int (const char * *pstr)
+read_int (const UCHAR_T * *pstr)
 {
-  unsigned int retval = **pstr - '0';
+  unsigned int retval = **pstr - L_('0');
 
-  while (isdigit (*++(*pstr)))
+  while (ISDIGIT (*++(*pstr)))
     {
       retval *= 10;
-      retval += **pstr - '0';
+      retval += **pstr - L_('0');
     }
 
   return retval;
@@ -91,13 +91,13 @@ read_int (const char * *pstr)
 /* Find the next spec in FORMAT, or the end of the string.  Returns
    a pointer into FORMAT, to a '%' or a '\0'.  */
 static inline const char *
-find_spec (const char *format)
+find_spec (const char *format, mbstate_t *ps)
 {
   while (*format != '\0' && *format != '%')
     {
       int len;
 
-      if (isascii (*format) || (len = mblen (format, MB_CUR_MAX)) <= 0)
+      if (isascii (*format) || (len = mbrlen (format, MB_CUR_MAX, ps)) <= 0)
 	++format;
       else
 	format += len;
@@ -116,8 +116,8 @@ extern printf_arginfo_function **__printf_arginfo_table;
    the number of args consumed by this spec; *MAX_REF_ARG is updated so it
    remains the highest argument index used.  */
 static inline size_t
-parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
-		size_t *max_ref_arg)
+parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec,
+		size_t *max_ref_arg, mbstate_t *ps)
 {
   unsigned int n;
   size_t nargs = 0;
@@ -135,13 +135,13 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
   spec->info.pad = ' ';
 
   /* Test for positional argument.  */
-  if (isdigit (*format))
+  if (ISDIGIT (*format))
     {
-      const char *begin = format;
+      const UCHAR_T *begin = format;
 
       n = read_int (&format);
 
-      if (n > 0 && *format == '$')
+      if (n > 0 && *format == L_('$'))
 	/* Is positional parameter.  */
 	{
 	  ++format;		/* Skip the '$'.  */
@@ -155,32 +155,32 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
     }
 
   /* Check for spec modifiers.  */
-  while (*format == ' ' || *format == '+' || *format == '-' ||
-	 *format == '#' || *format == '0' || *format == '\'')
+  while (*format == L_(' ') || *format == L_('+') || *format == L_('-') ||
+	 *format == L_('#') || *format == L_('0') || *format == L_('\''))
     switch (*format++)
       {
-      case ' ':
+      case L_(' '):
 	/* Output a space in place of a sign, when there is no sign.  */
 	spec->info.space = 1;
 	break;
-      case '+':
+      case L_('+'):
 	/* Always output + or - for numbers.  */
 	spec->info.showsign = 1;
 	break;
-      case '-':
+      case L_('-'):
 	/* Left-justify things.  */
 	spec->info.left = 1;
 	break;
-      case '#':
+      case L_('#'):
 	/* Use the "alternate form":
 	   Hex has 0x or 0X, FP always has a decimal point.  */
 	spec->info.alt = 1;
 	break;
-      case '0':
+      case L_('0'):
 	/* Pad with 0s.  */
 	spec->info.pad = '0';
 	break;
-      case '\'':
+      case L_('\''):
 	/* Show grouping in numbers if the locale information
 	   indicates any.  */
 	spec->info.group = 1;
@@ -192,18 +192,18 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
   /* Get the field width.  */
   spec->width_arg = -1;
   spec->info.width = 0;
-  if (*format == '*')
+  if (*format == L_('*'))
     {
       /* The field width is given in an argument.
 	 A negative field width indicates left justification.  */
-      const char *begin = ++format;
+      const UCHAR_T *begin = ++format;
 
-      if (isdigit (*format))
+      if (ISDIGIT (*format))
 	{
 	  /* The width argument might be found in a positional parameter.  */
 	  n = read_int (&format);
 
-	  if (n > 0 && *format == '$')
+	  if (n > 0 && *format == L_('$'))
 	    {
 	      spec->width_arg = n - 1;
 	      *max_ref_arg = MAX (*max_ref_arg, n);
@@ -219,7 +219,7 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
 	  format = begin;	/* Step back and reread.  */
 	}
     }
-  else if (isdigit (*format))
+  else if (ISDIGIT (*format))
     /* Constant width specification.  */
     spec->info.width = read_int (&format);
 
@@ -227,19 +227,19 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
   spec->prec_arg = -1;
   /* -1 means none given; 0 means explicit 0.  */
   spec->info.prec = -1;
-  if (*format == '.')
+  if (*format == L_('.'))
     {
       ++format;
-      if (*format == '*')
+      if (*format == L_('*'))
 	{
 	  /* The precision is given in an argument.  */
-	  const char *begin = ++format;
+	  const UCHAR_T *begin = ++format;
 
-	  if (isdigit (*format))
+	  if (ISDIGIT (*format))
 	    {
 	      n = read_int (&format);
 
-	      if (n > 0 && *format == '$')
+	      if (n > 0 && *format == L_('$'))
 		{
 		  spec->prec_arg = n - 1;
 		  *max_ref_arg = MAX (*max_ref_arg, n);
@@ -255,7 +255,7 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
 	      format = begin;
 	    }
 	}
-      else if (isdigit (*format))
+      else if (ISDIGIT (*format))
 	spec->info.prec = read_int (&format);
       else
 	/* "%.?" is treated like "%.0?".  */
@@ -268,40 +268,41 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
   spec->info.is_short = 0;
   spec->info.is_long = 0;
 
-  while (*format == 'h' || *format == 'l' || *format == 'L' ||
-	 *format == 'Z' || *format == 'q')
+  if (*format == L_('h') || *format == L_('l') || *format == L_('L') ||
+      *format == L_('Z') || *format == L_('q'))
     switch (*format++)
       {
-      case 'h':
+      case L_('h'):
 	/* int's are short int's.  */
 	spec->info.is_short = 1;
 	break;
-      case 'l':
-	if (spec->info.is_long)
-	  /* A double `l' is equivalent to an `L'.  */
-	  spec->info.is_longlong = 1;
-	else
-	  /* int's are long int's.  */
-	  spec->info.is_long = 1;
-	break;
-      case 'L':
+      case L_('l'):
+	/* int's are long int's.  */
+	spec->info.is_long = 1;
+	if (*format != L_('l'))
+	  break;
+	++format;
+	/* FALLTHROUGH */
+      case L_('L'):
 	/* double's are long double's, and int's are long long int's.  */
+      case L_('q'):
+	/* 4.4 uses this for long long.  */
 	spec->info.is_long_double = 1;
 	break;
-      case 'Z':
+      case L_('Z'):
 	/* int's are size_t's.  */
 	assert (sizeof(size_t) <= sizeof(unsigned long long int));
 	spec->info.is_longlong = sizeof(size_t) > sizeof(unsigned long int);
 	spec->info.is_long = sizeof(size_t) > sizeof(unsigned int);
 	break;
-      case 'q':
-	/* 4.4 uses this for long long.  */
-	spec->info.is_longlong = 1;
-	break;
       }
 
   /* Get the format specification.  */
-  spec->info.spec = *format++;
+#ifdef THIS_IS_INCOMPATIBLE_WITH_LINUX_LIBC
+  spec->info.spec = (wchar_t) *format++;
+#else
+  spec->info.spec = (char) *format++;
+#endif
   if (__printf_arginfo_table != NULL &&
       __printf_arginfo_table[spec->info.spec] != NULL)
     /* We don't try to get the types for all arguments if the format
@@ -315,12 +316,12 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
 
       switch (spec->info.spec)
 	{
-	case 'i':
-	case 'd':
-	case 'u':
-	case 'o':
-	case 'X':
-	case 'x':
+	case L'i':
+	case L'd':
+	case L'u':
+	case L'o':
+	case L'X':
+	case L'x':
 	  if (spec->info.is_longlong)
 	    spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG;
 	  else if (spec->info.is_long)
@@ -330,30 +331,30 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
 	  else
 	    spec->data_arg_type = PA_INT;
 	  break;
-	case 'e':
-	case 'E':
-	case 'f':
-	case 'g':
-	case 'G':
+	case L'e':
+	case L'E':
+	case L'f':
+	case L'g':
+	case L'G':
 	  if (spec->info.is_long_double)
 	    spec->data_arg_type = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
 	  else
 	    spec->data_arg_type = PA_DOUBLE;
 	  break;
-	case 'c':
+	case L'c':
 	  spec->data_arg_type = PA_CHAR;
 	  break;
-	case 's':
+	case L's':
 	  spec->data_arg_type = PA_STRING;
 	  break;
-	case 'p':
+	case L'p':
 	  spec->data_arg_type = PA_POINTER;
 	  break;
-	case 'n':
+	case L'n':
 	  spec->data_arg_type = PA_INT|PA_FLAG_PTR;
 	  break;
 
-	case 'm':
+	case L'm':
 	default:
 	  /* An unknown spec will consume no args.  */
 	  spec->ndata_args = 0;
@@ -370,14 +371,14 @@ parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
 	}
     }
 
-  if (spec->info.spec == '\0')
+  if (spec->info.spec == L'\0')
     /* Format ended before this spec was complete.  */
     spec->end_of_fmt = spec->next_fmt = format - 1;
   else
     {
       /* Find the next format spec.  */
       spec->end_of_fmt = format;
-      spec->next_fmt = find_spec (format);
+      spec->next_fmt = find_spec (format, ps);
     }
 
   return nargs;
diff --git a/stdio-common/printf-prs.c b/stdio-common/printf-prs.c
index 811a9cb7eb..d0756de7d4 100644
--- a/stdio-common/printf-prs.c
+++ b/stdio-common/printf-prs.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1992, 1995 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1992, 1995, 1996 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
 
 The GNU C Library is free software; you can redistribute it and/or
@@ -20,6 +20,50 @@ Cambridge, MA 02139, USA.  */
 #include <printf.h>
 #include <stdlib.h>
 #include <string.h>
+#include <wchar.h>
+
+#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 "printf-parse.h"
 
@@ -33,15 +77,17 @@ parse_printf_format (fmt, n, argtypes)
   size_t nargs;			/* Number of arguments.  */
   size_t max_ref_arg;		/* Highest index used in a positional arg.  */
   struct printf_spec spec;
+  mbstate_t mbstate;
 
   nargs = 0;
   max_ref_arg = 0;
+  mbstate = 0;
 
   /* Search for format specifications.  */
-  for (fmt = find_spec (fmt); *fmt != '\0'; fmt = spec.next_fmt)
+  for (fmt = find_spec (fmt, &mbstate); *fmt != '\0'; fmt = spec.next_fmt)
     {
       /* Parse this spec.  */
-      nargs += parse_one_spec (fmt, nargs, &spec, &max_ref_arg);
+      nargs += parse_one_spec (fmt, nargs, &spec, &max_ref_arg, &mbstate);
 
       /* If the width is determined by an argument this is an int.  */
       if (spec.width_arg != -1 && spec.width_arg < n)
diff --git a/stdio-common/printf.h b/stdio-common/printf.h
index df7747ec38..6e90154cde 100644
--- a/stdio-common/printf.h
+++ b/stdio-common/printf.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1992, 1993, 1995 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 92, 93, 95, 96 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
 
 The GNU C Library is free software; you can redistribute it and/or
@@ -26,6 +26,7 @@ __BEGIN_DECLS
 #define	__need_FILE
 #include <stdio.h>
 #define	__need_size_t
+#define __need_wchar_t
 #include <stddef.h>
 
 
@@ -33,7 +34,11 @@ struct printf_info
 {
   int prec;			/* Precision.  */
   int width;			/* Width.  */
-  unsigned char spec;		/* Format letter.  */
+#ifdef THIS_IS_INCOMPATIBLE_WITH_LINUX_LIBC
+  wchar_t spec;			/* Format letter.  */
+#else
+  char spec;			/* Format letter.  */
+#endif
   unsigned int is_long_double:1;/* L flag.  */
   unsigned int is_short:1;	/* h flag.  */
   unsigned int is_long:1;	/* l flag.  */
diff --git a/stdio-common/tst-printf.c b/stdio-common/tst-printf.c
index c177da18b2..12212b87b2 100644
--- a/stdio-common/tst-printf.c
+++ b/stdio-common/tst-printf.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1992, 1993, 1995 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1992, 1993, 1995, 1996 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
 
 The GNU C Library is free software; you can redistribute it and/or
@@ -188,7 +188,7 @@ I am ready for my first lesson today.";
   {
     double d = FLT_MIN;
     int niter = 17;
-    
+
     while (niter-- != 0)
       printf ("%.17e\n", d / 2);
     fflush (stdout);
@@ -233,7 +233,18 @@ I am ready for my first lesson today.";
   rfg1 ();
   rfg2 ();
 
-  exit(EXIT_SUCCESS);
+  {
+    char buf[200];
+    int result;
+
+    sprintf(buf,"%*s%*s%*s",-1,"one",-20,"two",-30,"three");
+
+    result = strcmp (buf,
+		     "onetwo                 three                         ");
+
+    puts (result != 0 ? "Test failed!" : "Test ok.");
+    return result != 0;
+  }
 }
 
 rfg1 ()
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index d6b9f9a092..26b31a6ed2 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -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
-
-
+
 /* 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)
 
 /* 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;
 }
-
-
+
 /* 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)