about summary refs log tree commit diff
path: root/stdio-common/vfprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdio-common/vfprintf.c')
-rw-r--r--stdio-common/vfprintf.c585
1 files changed, 375 insertions, 210 deletions
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 390ce91f71..fe145d6a3d 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -36,74 +36,16 @@
    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)							      \
-    { ssize_t __res = __printf_pad (s, (Padchar), width);		      \
-      if (__res == -1)							      \
-	{								      \
-	  done = -1;							      \
-	  goto all_done;						      \
-	}								      \
-      done += __res; }
-# 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)							      \
-    { ssize_t __res = __wprintf_pad (s, (Padchar), width);		      \
-      if (__res == -1)							      \
-	{								      \
-	  done = -1;							      \
-	  goto all_done;						      \
-	}								      \
-      done += __res; }
-# endif
-#endif
-
-/* Include the shared code for parsing the format string.  */
-#include "printf-parse.h"
-
 
 #ifdef USE_IN_LIBIO
 /* This code is for use in libio.  */
 # include <libioP.h>
-# define PUTC(C, F)	_IO_putc_unlocked (C, F)
-# define vfprintf	_IO_vfprintf
 # define FILE		_IO_FILE
 # undef va_list
 # define va_list	_IO_va_list
-# undef	BUFSIZ
+# undef BUFSIZ
 # define BUFSIZ		_IO_BUFSIZ
-# define ARGCHECK(S, Format)						      \
+# define ARGCHECK(S, Format) \
   do									      \
     {									      \
       /* Check file argument for consistence.  */			      \
@@ -120,11 +62,54 @@ ssize_t __wprintf_pad __P ((FILE *, wchar_t pad, size_t n));
 	}								      \
     } while (0)
 # define UNBUFFERED_P(S) ((S)->_IO_file_flags & _IO_UNBUFFERED)
+
+# ifndef COMPILE_WPRINTF
+#  define vfprintf	_IO_vfprintf
+#  define CHAR_T	char
+#  define UCHAR_T	unsigned char
+#  define INT_T		int
+#  define L_(Str)	Str
+#  define ISDIGIT(Ch)	isdigit (Ch)
+#  define ISASCII(Ch)	isascii (Ch)
+#  define MBRLEN(Cp, L, St) mbrlen (Cp, L, St)
+
+#  define PUT(F, S, N)	_IO_sputn ((F), (S), (N))
+#  define PAD(Padchar) \
+  if (width > 0)							      \
+    done += _IO_padn (s, (Padchar), width)
+#  define PUTC(C, F)	_IO_putc_unlocked (C, F)
+#  define ORIENT	if (_IO_fwide (s, -1) != -1) return -1
+# else
+# include "_itowa.h"
+
+#  define vfprintf	_IO_vfwprintf
+#  define CHAR_T	wchar_t
+/* This is a hack!!!  There should be a type uwchar_t.  */
+#  define UCHAR_T	unsigned int /* uwchar_t */
+#  define INT_T		wint_t
+#  define L_(Str)	L##Str
+#  define ISDIGIT(Ch)	iswdigit (Ch)
+#  define ISASCII(Ch)	(((unsigned int) (Ch) & ~0x7f) == 0)
+#  define MBRLEN(Cp, L, St) wcslen ((const wchar_t *) (Cp))
+
+#  define PUT(F, S, N)	_IO_sputn ((F), (S), (N))
+#  define PAD(Padchar) \
+  if (width > 0)							      \
+    done += _IO_wpadn (s, (Padchar), width)
+#  define PUTC(C, F)	_IO_putwc_unlocked (C, F)
+#  define ORIENT	if (_IO_fwide (s, 1) != 1) return -1
+
+#  define _itoa(Val, Buf, Base, Case) _itowa (Val, (wchar_t *) Buf, Base, Case)
+#  define _itoa_word(Val, Buf, Base, Case) _itowa_word (Val, (wchar_t *) Buf, \
+							Base, Case)
+#  undef EOF
+#  define EOF WEOF
+# endif
 #else /* ! USE_IN_LIBIO */
 /* This code is for use in the GNU C library.  */
 # include <stdio.h>
 # define PUT(F, S, N)	fwrite (S, 1, N, F)
-# define ARGCHECK(S, Format)						      \
+# define ARGCHECK(S, Format) \
   do									      \
     {									      \
       /* Check file argument for consistence.  */			      \
@@ -153,11 +138,14 @@ extern void __flockfile (FILE *);
 extern void __funlockfile (FILE *);
 #endif /* USE_IN_LIBIO */
 
+/* Include the shared code for parsing the format string.  */
+#include "printf-parse.h"
+
 
 #define	outchar(Ch)							      \
   do									      \
     {									      \
-      register const int outc = (Ch);					      \
+      register const INT_T outc = (Ch);					      \
       if (PUTC (outc, s) == EOF)					      \
 	{								      \
 	  done = -1;							      \
@@ -199,7 +187,7 @@ extern void __funlockfile (FILE *);
 
 
 /* Global variables.  */
-static const char null[] = "(null)";
+static const CHAR_T null[] = L_("(null)");
 
 
 /* Helper function to provide temporary buffering for unbuffered streams.  */
@@ -211,7 +199,8 @@ static int printf_unknown __P ((FILE *, const struct printf_info *,
 				const void *const *));
 
 /* Group digits of number string.  */
-static char *group_number __P ((CHAR_T *, CHAR_T *, const CHAR_T *, wchar_t))
+static UCHAR_T *group_number __P ((UCHAR_T *, UCHAR_T *, const char *,
+				   wchar_t))
      internal_function;
 
 
@@ -238,11 +227,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
   const UCHAR_T *end_of_spec;
 
   /* Buffer intermediate results.  */
-  char work_buffer[1000];
-  char *workend;
+  UCHAR_T work_buffer[1000];
+  UCHAR_T *workend;
 
   /* State for restartable multibyte character handling functions.  */
+#ifndef COMPILE_WPRINTF
   mbstate_t mbstate;
+#endif
 
   /* We have to save the original argument pointer.  */
   va_list ap_save;
@@ -505,7 +496,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	 now process the wanted format specifier.  */			      \
     LABEL (form_percent):						      \
       /* Write a literal "%".  */					      \
-      outchar ('%');							      \
+      outchar (L_('%'));						      \
       break;								      \
 									      \
     LABEL (form_integer):						      \
@@ -588,7 +579,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	  else								      \
 	    /* We have to take care for the '0' flag.  If a precision	      \
 	       is given it must be ignored.  */				      \
-	    pad = ' ';							      \
+	    pad = L_(' ');						      \
 									      \
 	  /* If the precision is 0 and the number is 0 nothing has to	      \
 	     be written for the number, except for the 'o' format in	      \
@@ -597,13 +588,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    {								      \
 	      string = workend;						      \
 	      if (base == 8 && alt)					      \
-		*string-- = '0';					      \
+		*string-- = L_('0');					      \
 	    }								      \
 	  else								      \
 	    {								      \
 	      /* Put the number in WORK.  */				      \
-	      string = _itoa (number.longlong, workend + 1, base,	      \
-			      spec == 'X');				      \
+	      string = (UCHAR_T *) _itoa (number.longlong, workend + 1, base, \
+					  spec == L_('X'));		      \
 	      string -= 1;						      \
 	      if (group && grouping)					      \
 		string = group_number (string, workend, grouping,	      \
@@ -642,7 +633,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	  else								      \
 	    /* We have to take care for the '0' flag.  If a precision	      \
 	       is given it must be ignored.  */				      \
-	    pad = ' ';							      \
+	    pad = L_(' ');						      \
 									      \
 	  /* If the precision is 0 and the number is 0 nothing has to	      \
 	     be written for the number, except for the 'o' format in	      \
@@ -651,13 +642,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    {								      \
 	      string = workend;						      \
 	      if (base == 8 && alt)					      \
-		*string-- = '0';					      \
+		*string-- = L_('0');					      \
 	    }								      \
 	  else								      \
 	    {								      \
 	      /* Put the number in WORK.  */				      \
-	      string = _itoa_word (number.word, workend + 1, base,	      \
-				   spec == 'X');			      \
+	      string = (UCHAR_T *) _itoa_word (number.word, workend + 1,      \
+					       base, spec == L_('X'));	      \
 	      string -= 1;						      \
 	      if (group && grouping)					      \
 		string = group_number (string, workend, grouping,	      \
@@ -670,10 +661,10 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       if (prec > 0)							      \
 	/* Add zeros to the precision.  */				      \
 	while (prec-- > 0)						      \
-	  *string-- = '0';						      \
+	  *string-- = L_('0');						      \
       else if (number.word != 0 && alt && base == 8)			      \
 	/* Add octal marker.  */					      \
-	*string-- = '0';						      \
+	*string-- = L_('0');						      \
 									      \
       if (!left)							      \
 	{								      \
@@ -686,41 +677,41 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	  if (is_negative || showsign || space)				      \
 	    --width;							      \
 									      \
-	  if (pad == '0')						      \
+	  if (pad == L_('0'))						      \
 	    {								      \
 	      while (width-- > 0)					      \
-		*string-- = '0';					      \
+		*string-- = L_('0');					      \
 									      \
 	      if (number.word != 0 && alt && base == 16)		      \
 		{							      \
 		  *string-- = spec;					      \
-		  *string-- = '0';					      \
+		  *string-- = L_('0');					      \
 		}							      \
 									      \
 	      if (is_negative)						      \
-		*string-- = '-';					      \
+		*string-- = L_('-');					      \
 	      else if (showsign)					      \
-		*string-- = '+';					      \
+		*string-- = L_('+');					      \
 	      else if (space)						      \
-		*string-- = ' ';					      \
+		*string-- = L_(' ');					      \
 	    }								      \
 	  else								      \
 	    {								      \
 	      if (number.word != 0 && alt && base == 16)		      \
 		{							      \
 		  *string-- = spec;					      \
-		  *string-- = '0';					      \
+		  *string-- = L_('0');					      \
 		}							      \
 									      \
 	      if (is_negative)						      \
-		*string-- = '-';					      \
+		*string-- = L_('-');					      \
 	      else if (showsign)					      \
-		*string-- = '+';					      \
+		*string-- = L_('+');					      \
 	      else if (space)						      \
-		*string-- = ' ';					      \
+		*string-- = L_(' ');					      \
 									      \
 	      while (width-- > 0)					      \
-		*string-- = ' ';					      \
+		*string-- = L_(' ');					      \
 	    }								      \
 									      \
 	  outstring (string + 1, workend - string);			      \
@@ -732,20 +723,20 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	  if (number.word != 0 && alt && base == 16)			      \
 	    {								      \
 	      *string-- = spec;						      \
-	      *string-- = '0';						      \
+	      *string-- = L_('0');					      \
 	    }								      \
 									      \
 	  if (is_negative)						      \
-	    *string-- = '-';						      \
+	    *string-- = L_('-');					      \
 	  else if (showsign)						      \
-	    *string-- = '+';						      \
+	    *string-- = L_('+');					      \
 	  else if (space)						      \
-	    *string-- = ' ';						      \
+	    *string-- = L_(' ');					      \
 									      \
 	  width -= workend - string;					      \
 	  outstring (string + 1, workend - string);			      \
 									      \
-	  PAD (' ');							      \
+	  PAD (L_(' '));						      \
 	  break;							      \
 	}								      \
 									      \
@@ -771,7 +762,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 					showsign: showsign,		      \
 					group: group,			      \
 					pad: pad,			      \
-					extra: 0 };			      \
+					extra: 0,			      \
+					wide: sizeof (CHAR_T) != 1 };	      \
 									      \
 	    if (is_long_double)						      \
 	      the_arg.pa_long_double = va_arg (ap, long double);	      \
@@ -821,7 +813,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 					showsign: showsign,		      \
 					group: group,			      \
 					pad: pad,			      \
-					extra: 0 };			      \
+					extra: 0,			      \
+					wide: sizeof (CHAR_T) != 1 };	      \
 									      \
 	    if (is_long_double)						      \
 	      the_arg.pa_long_double = va_arg (ap, long double);	      \
@@ -849,6 +842,178 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       }									      \
       break;								      \
 									      \
+    LABEL (form_pointer):						      \
+      /* Generic pointer.  */						      \
+      {									      \
+	const void *ptr;						      \
+	if (fspec == NULL)						      \
+	  ptr = va_arg (ap, void *);					      \
+	else								      \
+	  ptr = args_value[fspec->data_arg].pa_pointer;			      \
+	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 = (UCHAR_T *) L_("(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 (fspec == NULL)						      \
+	{								      \
+	  if (is_longlong)						      \
+	    *(long long int *) va_arg (ap, void *) = done;		      \
+	  else if (is_long_num)						      \
+	    *(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;			      \
+	}								      \
+      else								      \
+	if (is_longlong)						      \
+	  *(long long int *) args_value[fspec->data_arg].pa_pointer = done;   \
+	else if (is_long_num)						      \
+	  *(long int *) args_value[fspec->data_arg].pa_pointer = done;	      \
+	else if (!is_short)						      \
+	  *(int *) args_value[fspec->data_arg].pa_pointer = done;	      \
+	else								      \
+	  *(short int *) args_value[fspec->data_arg].pa_pointer = done;	      \
+      break;								      \
+									      \
+    LABEL (form_strerror):						      \
+      /* Print description of error ERRNO.  */				      \
+      string =								      \
+	(UCHAR_T *) __strerror_r (save_errno, (char *) work_buffer,	      \
+				  sizeof work_buffer);			      \
+      is_long = 0;		/* This is no wide-char string.  */	      \
+      goto LABEL (print_string)
+
+#ifdef COMPILE_WPRINTF
+# define process_string_arg(fspec) \
+    LABEL (form_character):						      \
+      /* Character.  */							      \
+      if (is_long)							      \
+	goto LABEL (form_wcharacter);					      \
+      --width;	/* Account for the character itself.  */		      \
+      if (!left)							      \
+	PAD (L' ');							      \
+      if (fspec == NULL)						      \
+	outchar (btowc ((unsigned char) va_arg (ap, int))); /* Promoted.  */  \
+      else								      \
+	outchar (btowc ((unsigned char) args_value[fspec->data_arg].pa_char));\
+      if (left)								      \
+	PAD (L' ');							      \
+      break;								      \
+									      \
+    LABEL (form_wcharacter):						      \
+      {									      \
+	/* Wide character.  */						      \
+	--width;							      \
+	if (!left)							      \
+	  PAD (L' ');							      \
+        if (fspec == NULL)						      \
+	  outchar (va_arg (ap, wint_t));				      \
+	else								      \
+	  outchar (args_value[fspec->data_arg].pa_wchar);		      \
+	if (left)							      \
+	  PAD (L' ');							      \
+      }									      \
+      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.  */		      \
+	if (fspec == NULL)						      \
+	  string = (UCHAR_T *) va_arg (ap, const wchar_t *);		      \
+	else								      \
+	  string = (UCHAR_T *) args_value[fspec->data_arg].pa_wstring;	      \
+									      \
+	/* 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) / sizeof (null[0])) - 1)      \
+	      {								      \
+		string = (UCHAR_T *) null;				      \
+		len = (sizeof (null) / sizeof (null[0])) - 1;		      \
+	      }								      \
+	    else							      \
+	      {								      \
+		string = (UCHAR_T *) L"";				      \
+		len = 0;						      \
+	      }								      \
+	  }								      \
+	else if (!is_long && spec != L_('S'))				      \
+	  {								      \
+	    /* This is complicated.  We have to transform the multibyte	      \
+	       string into a wide character string.  */			      \
+	    const char *mbs = (const char *) string;			      \
+	    mbstate_t mbstate;						      \
+									      \
+	    len = prec == -1 ? strnlen (mbs, prec) : strlen (mbs);	      \
+									      \
+	    /* Allocate dynamically an array which definitely is long	      \
+	       enough for the wide character version.  */		      \
+	    string = (UCHAR_T *) alloca ((len + 1) * sizeof (wchar_t));	      \
+									      \
+	    memset (&mbstate, '\0', sizeof (mbstate_t));		      \
+	    len = __mbsrtowcs ((wchar_t *) string, &mbs, len + 1, &mbstate);  \
+	    if (len == (size_t) -1)					      \
+	      {								      \
+		/* Illegal multibyte character.  */			      \
+		done = -1;						      \
+		goto all_done;						      \
+	      }								      \
+	  }								      \
+	else								      \
+	  {								      \
+	    if (prec != -1)						      \
+	      /* Search for the end of the string, but don't search past      \
+		 the length specified by the precision.  */		      \
+	      len = __wcsnlen ((wchar_t *) string, prec);		      \
+	    else							      \
+	      len = __wcslen ((wchar_t *) string);			      \
+	  }								      \
+									      \
+	if ((width -= len) < 0)						      \
+	  {								      \
+	    outstring (string, len);					      \
+	    break;							      \
+	  }								      \
+									      \
+	if (!left)							      \
+	  PAD (L' ');							      \
+	outstring (string, len);					      \
+	if (left)							      \
+	  PAD (L' ');							      \
+      }									      \
+      break;
+#else
+# define process_string_arg(fspec) \
     LABEL (form_character):						      \
       /* Character.  */							      \
       if (is_long)							      \
@@ -917,7 +1082,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    if (prec != -1)						      \
 	      /* Search for the end of the string, but don't search past      \
 		 the length specified by the precision.  */		      \
-	      len = strnlen (string, prec);				      \
+	      len = __strnlen (string, prec);				      \
 	    else							      \
 	      len = strlen (string);					      \
 	  }								      \
@@ -939,7 +1104,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    s2 = (const wchar_t *) string;				      \
 	    string = alloca (len + 1);					      \
 	    (void) __wcsrtombs (string, &s2, len + 1, &mbstate);	      \
-	    if (prec < len)						      \
+	    if (prec > 0 && prec < len)					      \
 	      len = prec;						      \
 	  }								      \
 									      \
@@ -955,75 +1120,23 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	if (left)							      \
 	  PAD (' ');							      \
       }									      \
-      break;								      \
-									      \
-    LABEL (form_pointer):						      \
-      /* Generic pointer.  */						      \
-      {									      \
-	const void *ptr;						      \
-	if (fspec == NULL)						      \
-	  ptr = va_arg (ap, void *);					      \
-	else								      \
-	  ptr = args_value[fspec->data_arg].pa_pointer;			      \
-	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 (fspec == NULL)						      \
-	{								      \
-	  if (is_longlong)						      \
-	    *(long long int *) va_arg (ap, void *) = done;		      \
-	  else if (is_long_num)						      \
-	    *(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;			      \
-	}								      \
-      else								      \
-	if (is_longlong)						      \
-	  *(long long int *) args_value[fspec->data_arg].pa_pointer = done;   \
-	else if (is_long_num)						      \
-	  *(long int *) args_value[fspec->data_arg].pa_pointer = done;	      \
-	else if (!is_short)						      \
-	  *(int *) args_value[fspec->data_arg].pa_pointer = done;	      \
-	else								      \
-	  *(short int *) args_value[fspec->data_arg].pa_pointer = done;	      \
-      break;								      \
-									      \
-    LABEL (form_strerror):						      \
-      /* Print description of error ERRNO.  */				      \
-      string =								      \
-	(char *) __strerror_r (save_errno, work_buffer, sizeof work_buffer);  \
-      is_long = 0;		/* This is no wide-char string.  */	      \
-      goto LABEL (print_string)
+      break;
+#endif
 
+  /* Orient the stream.  */
+#ifdef ORIENT
+  ORIENT;
+#endif
 
   /* Sanity check of arguments.  */
   ARGCHECK (s, format);
 
+  /* Check for correct orientation.  */
+  if (_IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
+      != (sizeof (CHAR_T) == 1 ? -1 : 1))
+    /* The stream is already oriented otherwise.  */
+    return EOF;
+
   if (UNBUFFERED_P (s))
     /* Use a helper function which will allocate a local temporary buffer
        for the stream and then call us again.  */
@@ -1041,11 +1154,16 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 #endif
   nspecs_done = 0;
 
+#ifdef COMPILE_WPRINTF
+  /* Find the first format specifier.  */
+  f = lead_str_end = find_spec ((const UCHAR_T *) format);
+#else
   /* Put state for processing format string in initial state.  */
   memset (&mbstate, '\0', sizeof (mbstate_t));
 
   /* Find the first format specifier.  */
   f = lead_str_end = find_spec (format, &mbstate);
+#endif
 
   /* Lock stream.  */
 #ifdef USE_IN_LIBIO
@@ -1081,7 +1199,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       } number;
       int base;
       union printf_arg the_arg;
-      char *string;	/* Pointer to argument string.  */
+      UCHAR_T *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.  */
@@ -1093,10 +1211,10 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       int is_char = 0;	/* Argument is promoted (unsigned) char.  */
       int width = 0;	/* Width of output; 0 means none specified.  */
       int prec = -1;	/* Precision of output; -1 means none specified.  */
-      char pad = ' ';	/* Padding character.  */
+      UCHAR_T pad = L_(' ');/* Padding character.  */
       CHAR_T spec;
 
-      workend = &work_buffer[sizeof (work_buffer) - 1];
+      workend = &work_buffer[sizeof (work_buffer) / sizeof (CHAR_T) - 1];
 
       /* Get current character in format string.  */
       JUMP (*++f, step0_jumps);
@@ -1172,10 +1290,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    left = 1;
 	  }
 
-	if (width + 32 >= sizeof (work_buffer))
+	if (width + 32 >= sizeof (work_buffer) / sizeof (work_buffer[0]))
 	  /* We have to use a special buffer.  The "32" is just a safe
 	     bet for all the output which is not counted in the width.  */
-	  workend = alloca (width + 32) + (width + 31);
+	  workend = ((UCHAR_T *) alloca ((width + 32) * sizeof (CHAR_T))
+		     + (width + 31));
       }
       JUMP (*f, step1_jumps);
 
@@ -1183,10 +1302,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
     LABEL (width):
       width = read_int (&f);
 
-      if (width + 32 >= sizeof (work_buffer))
+      if (width + 32 >= sizeof (work_buffer) / sizeof (work_buffer[0]))
 	/* We have to use a special buffer.  The "32" is just a safe
 	   bet for all the output which is not counted in the width.  */
-	workend = alloca (width + 32) + (width + 31);
+	workend = ((UCHAR_T *) alloca ((width + 32) * sizeof (CHAR_T))
+		   + (width + 31));
       if (*f == L_('$'))
 	/* Oh, oh.  The argument comes from a positional parameter.  */
 	goto do_positional;
@@ -1213,7 +1333,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	prec = read_int (&f);
       else
 	prec = 0;
-      if (prec > width && prec + 32 > sizeof (work_buffer))
+      if (prec > width
+	  && prec + 32 > sizeof (work_buffer) / sizeof (work_buffer[0]))
 	workend = alloca (spec + 32) + (spec + 31);
       JUMP (*f, step2_jumps);
 
@@ -1258,6 +1379,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       while (1)
 	{
 	  process_arg (((struct printf_spec *) NULL));
+	  process_string_arg (((struct printf_spec *) NULL));
 
 	LABEL (form_unknown):
 	  if (spec == L_('\0'))
@@ -1276,7 +1398,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       ++nspecs_done;
 
       /* Look for next format specifier.  */
+#ifdef COMPILE_WPRINTF
+      f = find_spec ((end_of_spec = ++f));
+#else
       f = find_spec ((end_of_spec = ++f), &mbstate);
+#endif
 
       /* Write the following constant string.  */
       outstring (end_of_spec, f - end_of_spec);
@@ -1301,7 +1427,7 @@ do_positional:
        attributes.  */
     size_t nargs = 0;
     int *args_type;
-    union printf_arg *args_value;
+    union printf_arg *args_value = NULL;
 
     /* Positional parameters refer to arguments directly.  This could
        also determine the maximum number of arguments.  Track the
@@ -1329,7 +1455,7 @@ do_positional:
 	  grouping = NULL;
       }
 
-    for (f = lead_str_end; *f != '\0'; f = specs[nspecs++].next_fmt)
+    for (f = lead_str_end; *f != L_('\0'); f = specs[nspecs++].next_fmt)
       {
 	if (nspecs >= nspecs_max)
 	  {
@@ -1356,8 +1482,12 @@ do_positional:
 	  }
 
 	/* Parse the format specifier.  */
+#ifdef COMPILE_WPRINTF
+	nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg);
+#else
 	nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg,
 				 &mbstate);
+#endif
       }
 
     /* Determine the number of arguments the format string consumes.  */
@@ -1449,7 +1579,7 @@ do_positional:
 	} number;
 	int base;
 	union printf_arg the_arg;
-	char *string;	/* Pointer to argument string.  */
+	UCHAR_T *string;	/* Pointer to argument string.  */
 
 	/* Fill variables from values in struct.  */
 	int alt = specs[nspecs_done].info.alt;
@@ -1498,8 +1628,10 @@ do_positional:
 	  }
 
 	/* Maybe the buffer is too small.  */
-	if (MAX (prec, width) + 32 > sizeof (work_buffer))
-	  workend = alloca (MAX (prec, width) + 32) + (MAX (prec, width) + 31);
+	if (MAX (prec, width) + 32 > sizeof (work_buffer) / sizeof (UCHAR_T))
+	  workend = ((UCHAR_T *) alloca ((MAX (prec, width) + 32)
+					 * sizeof (UCHAR_T))
+		     + (MAX (prec, width) + 31));
 
 	/* Process format specifiers.  */
 	while (1)
@@ -1507,6 +1639,7 @@ do_positional:
 	    JUMP (spec, step4_jumps);
 
 	    process_arg ((&specs[nspecs_done]));
+	    process_string_arg ((&specs[nspecs_done]));
 
 	  LABEL (form_unknown):
 	    {
@@ -1564,21 +1697,6 @@ all_done:
 
   return done;
 }
-
-#ifdef USE_IN_LIBIO
-# undef vfprintf
-# ifdef strong_alias
-/* This is for glibc.  */
-strong_alias (_IO_vfprintf, vfprintf);
-# else
-#  if defined __ELF__ || defined __GNU_LIBRARY__
-#   include <gnu-stabs.h>
-#   ifdef weak_alias
-weak_alias (_IO_vfprintf, vfprintf);
-#   endif
-#  endif
-# endif
-#endif
 
 /* Handle an unknown format specifier.  This prints out a canonicalized
    representation of the format spec itself.  */
@@ -1588,24 +1706,25 @@ printf_unknown (FILE *s, const struct printf_info *info,
 
 {
   int done = 0;
-  char work_buffer[MAX (info->width, info->spec) + 32];
-  char *const workend = &work_buffer[sizeof (work_buffer) - 1];
-  register char *w;
+  CHAR_T work_buffer[MAX (info->width, info->spec) + 32];
+  CHAR_T *const workend = &work_buffer[sizeof (work_buffer) / sizeof (CHAR_T)
+				      - 1];
+  register CHAR_T *w;
 
-  outchar ('%');
+  outchar (L_('%'));
 
   if (info->alt)
-    outchar ('#');
+    outchar (L_('#'));
   if (info->group)
-    outchar ('\'');
+    outchar (L_('\''));
   if (info->showsign)
-    outchar ('+');
+    outchar (L_('+'));
   else if (info->space)
-    outchar (' ');
+    outchar (L_(' '));
   if (info->left)
-    outchar ('-');
+    outchar (L_('-'));
   if (info->pad == '0')
-    outchar ('0');
+    outchar (L_('0'));
 
   if (info->width != 0)
     {
@@ -1622,7 +1741,7 @@ printf_unknown (FILE *s, const struct printf_info *info,
 	outchar (*w++);
     }
 
-  if (info->spec != '\0')
+  if (info->spec != L_('\0'))
     outchar (info->spec);
 
  all_done:
@@ -1631,13 +1750,13 @@ printf_unknown (FILE *s, const struct printf_info *info,
 
 /* 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 *
+static UCHAR_T *
 internal_function
-group_number (CHAR_T *w, CHAR_T *rear_ptr, const CHAR_T *grouping,
+group_number (UCHAR_T *w, UCHAR_T *rear_ptr, const char *grouping,
 	      wchar_t thousands_sep)
 {
   int len;
-  char *src, *s;
+  UCHAR_T *src, *s;
 
   /* We treat all negative values like CHAR_MAX.  */
 
@@ -1648,8 +1767,9 @@ group_number (CHAR_T *w, CHAR_T *rear_ptr, const CHAR_T *grouping,
   len = *grouping;
 
   /* Copy existing string so that nothing gets overwritten.  */
-  src = (char *) alloca (rear_ptr - w);
-  s = (char *) __mempcpy (src, w + 1, rear_ptr - w) - 1;
+  src = (UCHAR_T *) alloca ((rear_ptr - w) * sizeof (UCHAR_T));
+  s = (UCHAR_T *) __mempcpy (src, w + 1,
+			     (rear_ptr - w) * sizeof (UCHAR_T)) - 1;
   w = rear_ptr;
 
   /* Process all characters in the string.  */
@@ -1699,12 +1819,22 @@ static int
 _IO_helper_overflow (_IO_FILE *s, int c)
 {
   _IO_FILE *target = ((struct helper_file*) s)->_put_stream;
+#ifdef COMPILE_WPRINTF
+  int used = s->_wide_data->_IO_write_ptr - s->_wide_data->_IO_write_base;
+  if (used)
+    {
+      _IO_size_t written = _IO_sputn (target, s->_wide_data->_IO_write_base,
+				      used);
+      s->_wide_data->_IO_write_ptr -= written;
+    }
+#else
   int used = s->_IO_write_ptr - s->_IO_write_base;
   if (used)
     {
       _IO_size_t written = _IO_sputn (target, s->_IO_write_base, used);
       s->_IO_write_ptr -= written;
     }
+#endif
   return PUTC (c, s);
 }
 
@@ -1735,16 +1865,18 @@ internal_function
 buffered_vfprintf (register _IO_FILE *s, const CHAR_T *format,
 		   _IO_va_list args)
 {
-  char buf[_IO_BUFSIZ];
+  CHAR_T buf[_IO_BUFSIZ];
   struct helper_file helper;
   register _IO_FILE *hp = (_IO_FILE *) &helper;
   int result, to_flush;
 
   /* Initialize helper.  */
   helper._put_stream = s;
-  hp->_IO_write_base = buf;
-  hp->_IO_write_ptr = buf;
-  hp->_IO_write_end = buf + sizeof buf;
+#ifdef COMPILE_WPRINTF
+  _IO_wsetp (hp, buf, buf + sizeof buf / sizeof (CHAR_T));
+#else
+  _IO_setp (hp, buf, buf + sizeof buf);
+#endif
   hp->_IO_file_flags = _IO_MAGIC|_IO_NO_READS;
 #if _IO_JUMPS_OFFSET
   hp->_vtable_offset = 0;
@@ -1756,14 +1888,24 @@ buffered_vfprintf (register _IO_FILE *s, const CHAR_T *format,
   _IO_JUMPS (hp) = (struct _IO_jump_t *) &_IO_helper_jumps;
 
   /* Now print to helper instead.  */
-  result = _IO_vfprintf (hp, format, args);
+  result = vfprintf (hp, format, args);
 
   /* Now flush anything from the helper to the S. */
+#ifdef COMPILE_WPRINTF
+  if ((to_flush = (hp->_wide_data->_IO_write_ptr
+		   - hp->_wide_data->_IO_write_base)) > 0)
+    {
+      if ((int) _IO_sputn (s, hp->_wide_data->_IO_write_base, to_flush)
+	  != to_flush)
+	return -1;
+    }
+#else
   if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0)
     {
       if ((int) _IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush)
 	return -1;
     }
+#endif
 
   return result;
 }
@@ -1826,3 +1968,26 @@ __wprintf_pad (FILE *s, wchar_t pad, size_t count)
 }
 #undef PADSIZE
 #endif /* USE_IN_LIBIO */
+
+#ifdef USE_IN_LIBIO
+# undef vfprintf
+# ifdef strong_alias
+/* This is for glibc.  */
+#  ifdef COMPILE_WPRINTF
+strong_alias (_IO_vfwprintf, vfwprintf);
+#  else
+strong_alias (_IO_vfprintf, vfprintf);
+#  endif
+# else
+#  if defined __ELF__ || defined __GNU_LIBRARY__
+#   include <gnu-stabs.h>
+#   ifdef weak_alias
+#    ifdef COMPILE_WPRINTF
+weak_alias (_IO_vfwprintf, vfwprintf);
+#    else
+weak_alias (_IO_vfprintf, vfprintf);
+#    endif
+#   endif
+#  endif
+# endif
+#endif