Rewrite to allow use in wide character context.
authordrepper <drepper>
Wed, 16 Jun 1999 22:40:25 +0000 (22:40 +0000)
committerdrepper <drepper>
Wed, 16 Jun 1999 22:40:25 +0000 (22:40 +0000)
stdio-common/vfprintf.c
stdio-common/vfscanf.c

index 390ce91..fe145d6 100644 (file)
    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
 \f
 /* 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,
 \f
 /* Group the digits according to the grouping rules of the current locale.
    The interpretation of GROUPING is as in `struct lconv' from <locale.h>.  */
-static char *
+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
index 0339edb..5caf616 100644 (file)
@@ -16,6 +16,7 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <limits.h>
 #include <ctype.h>
 
 # undef va_list
 # define va_list       _IO_va_list
-# define ungetc(c, s)  ((void) ((int) c == EOF                               \
+
+# ifdef COMPILE_WPRINTF
+#  define ungetc(c, s) ((void) ((int) c == WEOF                              \
+                                || (--read_in,                               \
+                                    _IO_sputbackwc (s, (unsigned char) c))))
+#  define inchar()     (c == EOF ? EOF                                       \
+                        : ((c = _IO_getwc_unlocked (s)),                     \
+                           (void) (c != EOF && ++read_in), c))
+
+#  define MEMCPY(d, s, n) wmemcpy (d, s, n)
+#  define ISSPACE(Ch)    iswspace (Ch)
+#  define ISDIGIT(Ch)    iswdigit (Ch)
+#  define ISXDIGIT(Ch)   iswxdigit (Ch)
+#  define UNGETC(Ch, S)   ungetwc (Ch, S)
+#  define TOLOWER(Ch)    towlower (Ch)
+#  define ORIENT         if (_IO_fwide (s, 1) != 1) return EOF
+#  define __strtoll_internal   __wcstoll_internal
+#  define __strtoull_internal  __wcstoull_internal
+#  define __strtol_internal    __wcstol_internal
+#  define __strtoul_internal   __wcstoul_internal
+#  define __strtold_internal   __wcstold_internal
+#  define __strtod_internal    __wcstod_internal
+#  define __strtof_internal    __wcstof_internal
+
+#  define L_(Str)        L##Str
+#  define CHAR_T         wchar_t
+#  define UCHAR_T        unsigned int
+#  define WINT_T         wint_t
+# else
+#  define ungetc(c, s) ((void) ((int) c == EOF                               \
                                 || (--read_in,                               \
                                     _IO_sputbackc (s, (unsigned char) c))))
-# define inchar()      (c == EOF ? EOF                                       \
+#  define inchar()     (c == EOF ? EOF                                       \
                         : ((c = _IO_getc_unlocked (s)),                      \
                            (void) (c != EOF && ++read_in), c))
-# define encode_error()        do {                                                  \
+#  define MEMCPY(d, s, n) memcpy (d, s, n)
+#  define ISSPACE(Ch)    isspace (Ch)
+#  define ISDIGIT(Ch)    isdigit (Ch)
+#  define ISXDIGIT(Ch)   isxdigit (Ch)
+#  define UNGETC(Ch, S)   ungetc (Ch, S)
+#  define TOLOWER(Ch)    tolower (Ch)
+#  define ORIENT         if (_IO_fwide (s, -1) != -1) return EOF
+
+#  define L_(Str)        Str
+#  define CHAR_T         char
+#  define UCHAR_T        unsigned char
+#  define WINT_T         int
+# endif
+
+# define encode_error() do {                                                 \
                          if (errp != NULL) *errp |= 4;                       \
                          _IO_funlockfile (s);                                \
                          __libc_cleanup_end (0);                             \
                          __libc_cleanup_end (0);                             \
                          return done ?: EOF;                                 \
                        } while (0)
-# define memory_error()        do {                                                  \
+# define memory_error() do {                                                 \
                          _IO_funlockfile (s);                                \
                          __set_errno (ENOMEM);                               \
                          __libc_cleanup_end (0);                             \
    FORMAT, using the argument list in ARG.
    Return the number of assignments made, or -1 for an input error.  */
 #ifdef USE_IN_LIBIO
+# ifdef COMPILE_WPRINTF
+int
+_IO_vfwscanf (s, format, argptr, errp)
+     _IO_FILE *s;
+     const wchar_t *format;
+     _IO_va_list argptr;
+     int *errp;
+# else
 int
 _IO_vfscanf (s, format, argptr, errp)
      _IO_FILE *s;
      const char *format;
      _IO_va_list argptr;
      int *errp;
+# endif
 #else
 int
 __vfscanf (FILE *s, const char *format, va_list argptr)
 #endif
 {
   va_list arg;
-  register const char *f = format;
-  register unsigned char fc;   /* Current character of the format.  */
+  register const CHAR_T *f = format;
+  register UCHAR_T fc; /* Current character of the format.  */
   register size_t done = 0;    /* Assignments done.  */
   register size_t read_in = 0; /* Chars read in.  */
-  register int c = 0;          /* Last char read.  */
+  register WINT_T c = 0;       /* Last char read.  */
   register int width;          /* Maximum field width.  */
   register int flags;          /* Modifiers for current format element.  */
 
   /* Status for reading F-P nums.  */
   char got_dot, got_e, negative;
   /* If a [...] is a [^...].  */
-  char not_in;
+  CHAR_T not_in;
 #define exp_char not_in
   /* Base for integral numbers.  */
   int base;
@@ -236,8 +289,8 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
   /* Nonzero if we are reading a pointer.  */
   int read_pointer;
   /* Workspace.  */
-  char *tw;                    /* Temporary pointer.  */
-  char *wp = NULL;             /* Workspace.  */
+  CHAR_T *tw;                  /* Temporary pointer.  */
+  CHAR_T *wp = NULL;           /* Workspace.  */
   size_t wpmax = 0;            /* Maximal size of workspace.  */
   size_t wpsize;               /* Currently used bytes in workspace.  */
 #define ADDW(Ch)                                                           \
@@ -245,11 +298,11 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
     {                                                                      \
       if (wpsize == wpmax)                                                 \
        {                                                                   \
-         char *old = wp;                                                   \
+         CHAR_T *old = wp;                                                 \
          wpmax = UCHAR_MAX > 2 * wpmax ? UCHAR_MAX : 2 * wpmax;            \
-         wp = (char *) alloca (wpmax);                                     \
+         wp = (CHAR_T *) alloca (wpmax * sizeof (wchar_t));                \
          if (old != NULL)                                                  \
-           memcpy (wp, old, wpsize);                                       \
+           MEMCPY (wp, old, wpsize);                                       \
        }                                                                   \
       wp[wpsize++] = (Ch);                                                 \
     }                                                                      \
@@ -261,6 +314,10 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
   arg = (va_list) argptr;
 #endif
 
+#ifdef ORIENT
+  ORIENT;
+#endif
+
   ARGCHECK (s, format);
 
   /* Figure out the decimal point character.  */
@@ -280,8 +337,10 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
   LOCK_STREAM (s);
 
 
+#ifndef COMPILE_WPRINTF
   /* From now on we use `state' to convert the format string.  */
   memset (&state, '\0', sizeof (state));
+#endif
 
   /* Run through the format string.  */
   while (*f != '\0')
@@ -320,6 +379,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 # endif
 #endif
 
+#ifndef COMPILE_WPRINTF
       if (!isascii (*f))
        {
          /* Non-ASCII, may be a multibyte.  */
@@ -341,12 +401,13 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
              continue;
            }
        }
+#endif
 
       fc = *f++;
       if (fc != '%')
        {
          /* Remember to skip spaces.  */
-         if (isspace (fc))
+         if (ISSPACE (fc))
            {
              skip_space = 1;
              continue;
@@ -363,7 +424,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
             string.  Now it's time to skip all leading white space.  */
          if (skip_space)
            {
-             while (isspace (c))
+             while (ISSPACE (c))
                if (inchar () == EOF && errno == EINTR)
                  conv_error ();
              skip_space = 0;
@@ -371,7 +432,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 
          if (c != fc)
            {
-             ungetc (c, s);
+             UNGETC (c, s);
              conv_error ();
            }
 
@@ -391,12 +452,12 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
       wpsize = 0;
 
       /* Check for a positional parameter specification.  */
-      if (isdigit (*f))
+      if (ISDIGIT (*f))
        {
-         argpos = *f++ - '0';
-         while (isdigit (*f))
-           argpos = argpos * 10 + (*f++ - '0');
-         if (*f == '$')
+         argpos = *f++ - L_('0');
+         while (ISDIGIT (*f))
+           argpos = argpos * 10 + (*f++ - L_('0'));
+         if (*f == L_('$'))
            ++f;
          else
            {
@@ -409,27 +470,27 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
        }
 
       /* Check for the assignment-suppressing and the number grouping flag.  */
-      while (*f == '*' || *f == '\'')
+      while (*f == L_('*') || *f == L_('\''))
        switch (*f++)
          {
-         case '*':
+         case L_('*'):
            flags |= SUPPRESS;
            break;
-         case '\'':
+         case L_('\''):
            flags |= GROUP;
            break;
          }
 
       /* We have seen width. */
-      if (isdigit (*f))
+      if (ISDIGIT (*f))
        flags |= WIDTH;
 
       /* Find the maximum field width.  */
       width = 0;
-      while (isdigit (*f))
+      while (ISDIGIT (*f))
        {
          width *= 10;
-         width += *f++ - '0';
+         width += *f++ - L_('0');
        }
     got_width:
       if (width == 0)
@@ -438,9 +499,9 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
       /* Check for type modifiers.  */
       switch (*f++)
        {
-       case 'h':
+       case L_('h'):
          /* ints are short ints or chars.  */
-         if (*f == 'h')
+         if (*f == L_('h'))
            {
              ++f;
              flags |= CHAR;
@@ -448,8 +509,8 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
          else
            flags |= SHORT;
          break;
-       case 'l':
-         if (*f == 'l')
+       case L_('l'):
+         if (*f == L_('l'))
            {
              /* A double `l' is equivalent to an `L'.  */
              ++f;
@@ -459,15 +520,15 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            /* ints are long ints.  */
            flags |= LONG;
          break;
-       case 'q':
-       case 'L':
+       case L_('q'):
+       case L_('L'):
          /* doubles are long doubles, and ints are long long ints.  */
          flags |= LONGDBL | LONG;
          break;
-       case 'a':
+       case L_('a'):
          /* The `a' is used as a flag only if followed by `s', `S' or
             `['.  */
-         if (*f != 's' && *f != 'S' && *f != '[')
+         if (*f != L_('s') && *f != L_('S') && *f != L_('['))
            {
              --f;
              break;
@@ -476,19 +537,19 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
             arg and fill it in with a malloc'd pointer.  */
          flags |= MALLOC;
          break;
-       case 'z':
+       case L_('z'):
          if (need_longlong && sizeof (size_t) > sizeof (unsigned long int))
            flags |= LONGDBL;
          else if (sizeof (size_t) > sizeof (unsigned int))
            flags |= LONG;
          break;
-       case 'j':
+       case L_('j'):
          if (need_longlong && sizeof (uintmax_t) > sizeof (unsigned long int))
            flags |= LONGDBL;
          else if (sizeof (uintmax_t) > sizeof (unsigned int))
            flags |= LONG;
          break;
-       case 't':
+       case L_('t'):
          if (need_longlong && sizeof (ptrdiff_t) > sizeof (long int))
            flags |= LONGDBL;
          else if (sizeof (ptrdiff_t) > sizeof (int))
@@ -501,12 +562,13 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
        }
 
       /* End of the format string?  */
-      if (*f == '\0')
+      if (*f == L_('\0'))
        conv_error ();
 
       /* Find the conversion specifier.  */
       fc = *f++;
-      if (skip_space || (fc != '[' && fc != 'c' && fc != 'C' && fc != 'n'))
+      if (skip_space || (fc != L_('[') && fc != L_('c')
+                        && fc != L_('C') && fc != L_('n')))
        {
          /* Eat whitespace.  */
          int save_errno = errno;
@@ -514,15 +576,15 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
          do
            if (inchar () == EOF && errno == EINTR)
              input_error ();
-         while (isspace (c));
+         while (ISSPACE (c));
          errno = save_errno;
-         ungetc (c, s);
+         UNGETC (c, s);
          skip_space = 0;
        }
 
       switch (fc)
        {
-       case '%':       /* Must match a literal '%'.  */
+       case L_('%'):   /* Must match a literal '%'.  */
          c = inchar ();
          if (c == EOF)
            input_error ();
@@ -533,7 +595,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            }
          break;
 
-       case 'n':       /* Answer number of assignments done.  */
+       case L_('n'):   /* Answer number of assignments done.  */
          /* Corrigendum 1 to ISO C 1990 describes the allowed flags
             with the 'n' conversion specifier.  */
          if (!(flags & SUPPRESS))
@@ -581,7 +643,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            }
          break;
 
-       case 'c':       /* Match characters.  */
+       case L_('c'):   /* Match characters.  */
          if ((flags & LONG) == 0)
            {
              if (!(flags & SUPPRESS))
@@ -598,6 +660,26 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
              if (width == -1)
                width = 1;
 
+#ifdef COMPILE_WPRINTF
+             /* We have to convert the wide character(s) into multibyte
+                characters and store the result.  */
+             memset (&state, '\0', sizeof (state));
+
+             do
+               {
+                 size_t n;
+
+                 n = wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state);
+                 if (n == (size_t) -1)
+                   /* No valid wide character.  */
+                   input_error ();
+
+                 /* Increment the output pointer.  Even if we don't
+                    write anything.  */
+                 str += n;
+               }
+             while (--width > 0 && inchar () != EOF);
+#else
              if (!(flags & SUPPRESS))
                {
                  do
@@ -606,6 +688,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
                }
              else
                while (--width > 0 && inchar () != EOF);
+#endif
 
              if (!(flags & SUPPRESS))
                ++done;
@@ -613,238 +696,448 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
              break;
            }
          /* FALLTHROUGH */
-       case 'C':
-         /* Get UTF-8 encoded wide character.  Here we assume (as in
-            other parts of the libc) that we only have to handle
-            UTF-8.  */
+       case L_('C'):
+         if (!(flags & SUPPRESS))
+           {
+             wstr = ARG (wchar_t *);
+             if (str == NULL)
+               conv_error ();
+           }
+
+         c = inchar ();
+         if (c == EOF)
+           input_error ();
+
+#ifdef COMPILE_WPRINTF
+         /* Just store the incoming wide characters.  */
+         if (!(flags & SUPPRESS))
+           {
+             do
+               *wstr++ = c;
+             while (--width > 0 && inchar () != EOF);
+           }
+         else
+           while (--width > 0 && inchar () != EOF);
+#else
          {
-           wint_t val;
-           size_t cnt = 0;
-           int first = 1;
+           /* We have to convert the multibyte input sequence to wide
+              characters.  */
+           char buf[MB_LEN_MAX];
+           mbstate_t cstate;
 
-           if (!(flags & SUPPRESS))
-             {
-               wstr = ARG (wchar_t *);
-               if (str == NULL)
-                 conv_error ();
-             }
+           memset (&cstate, '\0', sizeof (cstate));
 
            do
              {
-#define NEXT_WIDE_CHAR(First)                                                \
-               c = inchar ();                                                \
-               if (c == EOF)                                                 \
-                 {                                                           \
-                   /* EOF is only an error for the first character.  */      \
-                   if (First)                                                \
-                     input_error ();                                         \
-                   else                                                      \
-                     break;                                                  \
-                 }                                                           \
-               val = c;                                                      \
-               if (val >= 0x80)                                              \
-                 {                                                           \
-                   if ((c & 0xc0) == 0x80 || (c & 0xfe) == 0xfe)             \
-                     encode_error ();                                        \
-                   if ((c & 0xe0) == 0xc0)                                   \
-                     {                                                       \
-                       /* We expect two bytes.  */                           \
-                       cnt = 1;                                              \
-                       val &= 0x1f;                                          \
-                     }                                                       \
-                   else if ((c & 0xf0) == 0xe0)                              \
-                     {                                                       \
-                       /* We expect three bytes.  */                         \
-                       cnt = 2;                                              \
-                       val &= 0x0f;                                          \
-                     }                                                       \
-                   else if ((c & 0xf8) == 0xf0)                              \
-                     {                                                       \
-                       /* We expect four bytes.  */                          \
-                       cnt = 3;                                              \
-                       val &= 0x07;                                          \
-                     }                                                       \
-                   else if ((c & 0xfc) == 0xf8)                              \
-                     {                                                       \
-                       /* We expect five bytes.  */                          \
-                       cnt = 4;                                              \
-                       val &= 0x03;                                          \
-                     }                                                       \
-                   else                                                      \
-                     {                                                       \
-                       /* We expect six bytes.  */                           \
-                       cnt = 5;                                              \
-                       val &= 0x01;                                          \
-                     }                                                       \
-                                                                             \
-                   do                                                        \
-                     {                                                       \
-                       c = inchar ();                                        \
-                       if (c == EOF                                          \
-                           || (c & 0xc0) == 0x80 || (c & 0xfe) == 0xfe)      \
-                         encode_error ();                                    \
-                       val <<= 6;                                            \
-                       val |= c & 0x3f;                                      \
-                     }                                                       \
-                   while (--cnt > 0);                                        \
-                 }                                                           \
-                                                                             \
-               if (!(flags & SUPPRESS))                                      \
-                 *wstr++ = val;                                              \
-               First = 0
-
-               NEXT_WIDE_CHAR (first);
-             }
-           while (--width > 0);
+               size_t cnt;
 
-           if (!(flags & SUPPRESS))
-             ++done;
+               /* This is what we present the mbrtowc function first.  */
+               buf[0] = c;
+               cnt = 1;
+
+               while (1)
+                 {
+                   size_t n;
+
+                   n = __mbrtowc (!(flags & SUPPRESS) ? wstr : NULL,
+                                  buf, cnt, &cstate);
+
+                   if (n == (size_t) -2)
+                     {
+                       /* Possibly correct character, just not enough
+                          input.  */
+                       assert (cnt < MB_CUR_MAX);
+
+                       if (inchar () == EOF)
+                         encode_error ();
+
+                       buf[cnt++] = c;
+                       continue;
+                     }
+
+                   if (n != cnt)
+                     encode_error ();
+
+                   /* We have a match.  */
+                   break;
+                 }
+
+               /* Advance the result pointer.  */
+               ++wstr;
+             }
+           while (--width > 0 && inchar () != EOF);
          }
-         break;
+#endif
 
-       case 's':               /* Read a string.  */
-         if (flags & LONG)
-           /* We have to process a wide character string.  */
-           goto wide_char_string;
+         if (!(flags & SUPPRESS))
+           ++done;
 
+         break;
+
+       case L_('s'):           /* Read a string.  */
+         if (!(flags & LONG))
+           {
 #define STRING_ARG(Str, Type)                                                \
-         if (!(flags & SUPPRESS))                                            \
-           {                                                                 \
-             if (flags & MALLOC)                                             \
+             do if (!(flags & SUPPRESS))                                     \
                {                                                             \
-                 /* The string is to be stored in a malloc'd buffer.  */     \
-                 strptr = ARG (char **);                                     \
-                 if (strptr == NULL)                                         \
+                 if (flags & MALLOC)                                         \
+                   {                                                         \
+                     /* The string is to be stored in a malloc'd buffer.  */ \
+                     strptr = ARG (char **);                                 \
+                     if (strptr == NULL)                                     \
+                       conv_error ();                                        \
+                     /* Allocate an initial buffer.  */                      \
+                     strsize = 100;                                          \
+                     *strptr = (char *) malloc (strsize * sizeof (Type));    \
+                     Str = (Type *) *strptr;                                 \
+                   }                                                         \
+                 else                                                        \
+                   Str = ARG (Type *);                                       \
+                 if (Str == NULL)                                            \
                    conv_error ();                                            \
-                 /* Allocate an initial buffer.  */                          \
-                 strsize = 100;                                              \
-                 *strptr = malloc (strsize * sizeof (Type));                 \
-                 Str = (Type *) *strptr;                                     \
-               }                                                             \
-             else                                                            \
-               Str = ARG (Type *);                                           \
-             if (Str == NULL)                                                \
-               conv_error ();                                                \
-           }
-         STRING_ARG (str, char);
+               } while (0)
+             STRING_ARG (str, char);
 
-         c = inchar ();
-         if (c == EOF)
-           input_error ();
+             c = inchar ();
+             if (c == EOF)
+               input_error ();
 
-         do
-           {
-             if (isspace (c))
+#ifdef COMPILE_WPRINTF
+             memset (&state, '\0', sizeof (state));
+#endif
+
+             do
                {
-                 ungetc (c, s);
-                 break;
-               }
-#define        STRING_ADD_CHAR(Str, c, Type)                                         \
-             if (!(flags & SUPPRESS))                                        \
-               {                                                             \
-                 *Str++ = c;                                                 \
-                 if ((flags & MALLOC) && (char *) Str == *strptr + strsize)  \
-                   {                                                         \
-                     /* Enlarge the buffer.  */                              \
-                     Str = realloc (*strptr, strsize * 2 * sizeof (Type));   \
-                     if (Str == NULL)                                        \
-                       {                                                     \
-                         /* Can't allocate that much.  Last-ditch effort.  */\
-                         Str = realloc (*strptr,                             \
-                                        (strsize + 1) * sizeof (Type));      \
-                         if (Str == NULL)                                    \
-                           {                                                 \
-                             /* We lose.  Oh well.                           \
-                                Terminate the string and stop converting,    \
-                                so at least we don't skip any input.  */     \
-                             ((Type *) (*strptr))[strsize] = '\0';           \
-                             ++done;                                         \
-                             conv_error ();                                  \
-                           }                                                 \
-                         else                                                \
-                           {                                                 \
-                             *strptr = (char *) Str;                         \
-                             Str = ((Type *) *strptr) + strsize;             \
-                             ++strsize;                                      \
-                           }                                                 \
-                       }                                                     \
-                     else                                                    \
-                       {                                                     \
-                         *strptr = (char *) Str;                             \
-                         Str = ((Type *) *strptr) + strsize;                 \
-                         strsize *= 2;                                       \
-                       }                                                     \
-                   }                                                         \
+                 if (ISSPACE (c))
+                   {
+                     UNGETC (c, s);
+                     break;
+                   }
+
+#ifdef COMPILE_WPRINTF
+                 /* This is quite complicated.  We have to convert the
+                    wide characters into multibyte characters and then
+                    store them.  */
+                 {
+                   size_t n;
+
+                   if (!(flags & SUPPRESS) && (flags & MALLOC)
+                       && str + MB_CUR_MAX >= *strptr + strsize)
+                     {
+                       /* We have to enlarge the buffer if the `a' flag
+                          was given.  */
+                       str = (char *) realloc (*strptr, strsize * 2);
+                       if (str == NULL)
+                         {
+                           /* Can't allocate that much.  Last-ditch
+                              effort.  */
+                           str = (char *) realloc (*strptr, strsize + 1);
+                           if (str == NULL)
+                             {
+                               /* We lose.  Oh well.  Terminate the
+                                  string and stop converting,
+                                  so at least we don't skip any input.  */
+                               ((char *) (*strptr))[strsize - 1] = '\0';
+                               ++done;
+                               conv_error ();
+                             }
+                           else
+                             {
+                               *strptr = (char *) str;
+                               str += strsize;
+                               ++strsize;
+                             }
+                         }
+                       else
+                         {
+                           *strptr = (char *) str;
+                           str += strsize;
+                           strsize *= 2;
+                         }
+                     }
+
+                   n = wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state);
+                   if (n == (size_t) -1)
+                     encode_error ();
+
+                   assert (n <= MB_CUR_MAX);
+                   str += n;
+                 }
+#else
+                 /* This is easy.  */
+                 if (!(flags & SUPPRESS))
+                   {
+                     *str++ = c;
+                     if ((flags & MALLOC)
+                         && (char *) str == *strptr + strsize)
+                       {
+                         /* Enlarge the buffer.  */
+                         str = (char *) realloc (*strptr, 2 * strsize);
+                         if (str == NULL)
+                           {
+                             /* Can't allocate that much.  Last-ditch
+                                effort.  */
+                             str = (char *) realloc (*strptr, strsize + 1);
+                             if (str == NULL)
+                               {
+                                 /* We lose.  Oh well.  Terminate the
+                                    string and stop converting,
+                                    so at least we don't skip any input.  */
+                                 ((char *) (*strptr))[strsize - 1] = '\0';
+                                 ++done;
+                                 conv_error ();
+                               }
+                             else
+                               {
+                                 *strptr = (char *) str;
+                                 str += strsize;
+                                 ++strsize;
+                               }
+                           }
+                         else
+                           {
+                             *strptr = (char *) str;
+                             str += strsize;
+                             strsize *= 2;
+                           }
+                       }
+                   }
+#endif
                }
-             STRING_ADD_CHAR (str, c, char);
-           } while ((width <= 0 || --width > 0) && inchar () != EOF);
+             while ((width <= 0 || --width > 0) && inchar () != EOF);
 
-         if (!(flags & SUPPRESS))
-           {
-             *str = '\0';
-             ++done;
+             if (!(flags & SUPPRESS))
+               {
+#ifdef COMPILE_WPRINTF
+                 /* We have to emit the code to get into the intial
+                    state.  */
+                 char buf[MB_LEN_MAX];
+                 size_t n = wcrtomb (buf, L'\0', &state);
+                 if (n > 0 && (flags & MALLOC)
+                     && str + n >= *strptr + strsize)
+                   {
+                     /* Enlarge the buffer.  */
+                     str = (char *) realloc (*strptr,
+                                             (str + n + 1) - *strptr);
+                     if (str == NULL)
+                       {
+                         /* We lose.  Oh well.  Terminate the string
+                            and stop converting, so at least we don't
+                            skip any input.  */
+                         ((char *) (*strptr))[strsize - 1] = '\0';
+                         ++done;
+                         conv_error ();
+                       }
+                     else
+                       {
+                         *strptr = (char *) str;
+                         str = ((char *) *strptr) + strsize;
+                         strsize = (str + n + 1) - *strptr;
+                       }
+                   }
+
+                 str = __mempcpy (str, buf, n);
+#endif
+                 *str = '\0';
+
+                 if ((flags & MALLOC) && str - *strptr != strsize)
+                   {
+                     char *cp = (char *) realloc (*strptr, str - *strptr);
+                     if (cp != NULL)
+                       *strptr = cp;
+                   }
+
+                 ++done;
+               }
+             break;
            }
-         break;
+         /* FALLTHROUGH */
 
-       case 'S':
-         /* Wide character string.  */
-       wide_char_string:
+       case L_('S'):
          {
-           wint_t val;
-           int first = 1;
+#ifndef COMPILE_WPRINTF
+           mbstate_t cstate;
+#endif
+
+           /* Wide character string.  */
            STRING_ARG (wstr, wchar_t);
 
+           c = inchar ();
+           if (c == EOF)
+             input_error ();
+
+#ifndef COMPILE_WPRINTF
+           memset (&cstate, '\0', sizeof (cstate));
+#endif
+
            do
              {
-               size_t cnt = 0;
-               NEXT_WIDE_CHAR (first);
-
-               if (__iswspace (val))
+               if (ISSPACE (c))
                  {
-                   /* XXX We would have to push back the whole wide char
-                      with possibly many bytes.  But since scanf does
-                      not make a difference for white space characters
-                      we can simply push back a simple <SP> which is
-                      guaranteed to be in the [:space:] class.  */
-                   ungetc (' ', s);
+                   UNGETC (c, s);
                    break;
                  }
 
-               STRING_ADD_CHAR (wstr, val, wchar_t);
-               first = 0;
+#ifdef COMPILE_WPRINTF
+               /* This is easy.  */
+               if (!(flags & SUPPRESS))
+                 {
+                   *wstr++ = c;
+                   if ((flags & MALLOC)
+                       && wstr == (wchar_t *) *strptr + strsize)
+                     {
+                       /* Enlarge the buffer.  */
+                       wstr = (wchar_t *) realloc (*strptr,
+                                                   (2 * strsize)
+                                                   * sizeof (wchar_t));
+                       if (wstr == NULL)
+                         {
+                           /* Can't allocate that much.  Last-ditch
+                               effort.  */
+                           wstr = (wchar_t *) realloc (*strptr,
+                                                       (strsize
+                                                        + sizeof (wchar_t)));
+                           if (wstr == NULL)
+                             {
+                               /* We lose.  Oh well.  Terminate the string
+                                  and stop converting, so at least we don't
+                                  skip any input.  */
+                               ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
+                               ++done;
+                               conv_error ();
+                             }
+                           else
+                             {
+                               *strptr = (char *) wstr;
+                               wstr += strsize;
+                               ++strsize;
+                             }
+                         }
+                       else
+                         {
+                           *strptr = (char *) wstr;
+                           wstr += strsize;
+                           strsize *= 2;
+                         }
+                     }
+                 }
+#else
+               {
+                 char buf[MB_LEN_MAX];
+                 size_t cnt;
+
+                 buf[0] = c;
+                 cnt = 1;
+
+                 while (1)
+                   {
+                     size_t n;
+
+                     n = __mbrtowc (!(flags & SUPPRESS) ? wstr : NULL,
+                                    buf, cnt, &cstate);
+
+                     if (n == (size_t) -2)
+                       {
+                         /* Possibly correct character, just not enough
+                            input.  */
+                         assert (cnt < MB_CUR_MAX);
+
+                         if (inchar () == EOF)
+                           encode_error ();
+
+                         buf[cnt++] = c;
+                         continue;
+                       }
+
+                     if (n != cnt)
+                       encode_error ();
+
+                     /* We have a match.  */
+                     break;
+                   }
+
+                 if (!(flags & SUPPRESS) && (flags & MALLOC)
+                     && wstr == (wchar_t *) *strptr + strsize)
+                   {
+                     /* Enlarge the buffer.  */
+                     wstr = (wchar_t *) realloc (*strptr,
+                                                 (2 * strsize
+                                                  * sizeof (wchar_t)));
+                     if (wstr == NULL)
+                       {
+                         /* Can't allocate that much.  Last-ditch effort.  */
+                         wstr = (wchar_t *) realloc (*strptr,
+                                                     ((strsize + 1)
+                                                      * sizeof (wchar_t)));
+                         if (wstr == NULL)
+                           {
+                             /* We lose.  Oh well.  Terminate the
+                                string and stop converting, so at
+                                least we don't skip any input.  */
+                             ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
+                             ++done;
+                             conv_error ();
+                           }
+                         else
+                           {
+                             *strptr = (char *) wstr;
+                             wstr += strsize;
+                             ++strsize;
+                           }
+                       }
+                     else
+                       {
+                         *strptr = (char *) wstr;
+                         wstr += strsize;
+                         strsize *= 2;
+                       }
+                   }
+               }
+#endif
              }
-           while (width <= 0 || --width > 0);
+           while ((width <= 0 || --width > 0) && inchar () != EOF);
 
            if (!(flags & SUPPRESS))
              {
-               *wstr = L'\0';
+               *wstr++ = L'\0';
+
+               if ((flags & MALLOC) && wstr - (wchar_t *) *strptr != strsize)
+                 {
+                   wchar_t *cp = (wchar_t *) realloc (*strptr,
+                                                      ((wstr
+                                                        - (wchar_t *) *strptr)
+                                                       * sizeof(wchar_t)));
+                   if (cp != NULL)
+                     *strptr = (char *) cp;
+                 }
+
                ++done;
              }
          }
          break;
 
-       case 'x':       /* Hexadecimal integer.  */
-       case 'X':       /* Ditto.  */
+       case L_('x'):   /* Hexadecimal integer.  */
+       case L_('X'):   /* Ditto.  */
          base = 16;
          number_signed = 0;
          goto number;
 
-       case 'o':       /* Octal integer.  */
+       case L_('o'):   /* Octal integer.  */
          base = 8;
          number_signed = 0;
          goto number;
 
-       case 'u':       /* Unsigned decimal integer.  */
+       case L_('u'):   /* Unsigned decimal integer.  */
          base = 10;
          number_signed = 0;
          goto number;
 
-       case 'd':       /* Signed decimal integer.  */
+       case L_('d'):   /* Signed decimal integer.  */
          base = 10;
          number_signed = 1;
          goto number;
 
-       case 'i':       /* Generic number.  */
+       case L_('i'):   /* Generic number.  */
          base = 0;
          number_signed = 1;
 
@@ -854,7 +1147,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            input_error ();
 
          /* Check for a sign.  */
-         if (c == '-' || c == '+')
+         if (c == L_('-') || c == L_('+'))
            {
              ADDW (c);
              if (width > 0)
@@ -863,7 +1156,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            }
 
          /* Look for a leading indication of base.  */
-         if (width != 0 && c == '0')
+         if (width != 0 && c == L_('0'))
            {
              if (width > 0)
                --width;
@@ -871,7 +1164,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
              ADDW (c);
              c = inchar ();
 
-             if (width != 0 && _tolower (c) == 'x')
+             if (width != 0 && TOLOWER (c) == L_('x'))
                {
                  if (base == 0)
                    base = 16;
@@ -892,8 +1185,8 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
          /* Read the number into workspace.  */
          while (c != EOF && width != 0)
            {
-             if (base == 16 ? !isxdigit (c) :
-                 ((!isdigit (c) || c - '0' >= base) &&
+             if (base == 16 ? !ISXDIGIT (c) :
+                 ((!ISDIGIT (c) || c - L_('0') >= base) &&
                   !((flags & GROUP) && base == 10 && c == thousands)))
                break;
              ADDW (c);
@@ -904,34 +1197,34 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            }
 
          if (wpsize == 0 ||
-             (wpsize == 1 && (wp[0] == '+' || wp[0] == '-')))
+             (wpsize == 1 && (wp[0] == L_('+') || wp[0] == L_('-'))))
            {
              /* There was no number.  If we are supposed to read a pointer
                 we must recognize "(nil)" as well.  */
              if (wpsize == 0 && read_pointer && (width < 0 || width >= 0)
                  && c == '('
-                 && _tolower (inchar ()) == 'n'
-                 && _tolower (inchar ()) == 'i'
-                 && _tolower (inchar ()) == 'l'
-                 && inchar () == ')')
+                 && TOLOWER (inchar ()) == L_('n')
+                 && TOLOWER (inchar ()) == L_('i')
+                 && TOLOWER (inchar ()) == L_('l')
+                 && inchar () == L_(')'))
                /* We must produce the value of a NULL pointer.  A single
                   '0' digit is enough.  */
-               ADDW ('0');
+               ADDW (L_('0'));
              else
                {
                  /* The last read character is not part of the number
                     anymore.  */
-                 ungetc (c, s);
+                 UNGETC (c, s);
 
                  conv_error ();
                }
            }
          else
            /* The just read character is not part of the number anymore.  */
-           ungetc (c, s);
+           UNGETC (c, s);
 
          /* Convert the number.  */
-         ADDW ('\0');
+         ADDW (L_('\0'));
          if (need_longlong && (flags & LONGDBL))
            {
              if (number_signed)
@@ -982,28 +1275,28 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            }
          break;
 
-       case 'e':       /* Floating-point numbers.  */
-       case 'E':
-       case 'f':
-       case 'g':
-       case 'G':
-       case 'a':
-       case 'A':
+       case L_('e'):   /* Floating-point numbers.  */
+       case L_('E'):
+       case L_('f'):
+       case L_('g'):
+       case L_('G'):
+       case L_('a'):
+       case L_('A'):
          c = inchar ();
          if (c == EOF)
            input_error ();
 
          /* Check for a sign.  */
-         if (c == '-' || c == '+')
+         if (c == L_('-') || c == L_('+'))
            {
-             negative = c == '-';
+             negative = c == L_('-');
              if (inchar () == EOF)
                /* EOF is only an input error before we read any chars.  */
                conv_error ();
-             if (! isdigit (c) && c != decimal)
+             if (! ISDIGIT (c) && c != decimal)
                {
                  /* This is no valid number.  */
-                 ungetc (c, s);
+                 UNGETC (c, s);
                  input_error ();
                }
              if (width > 0)
@@ -1013,69 +1306,69 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            negative = 0;
 
          /* Take care for the special arguments "nan" and "inf".  */
-         if (_tolower (c) == 'n')
+         if (TOLOWER (c) == L_('n'))
            {
              /* Maybe "nan".  */
              ADDW (c);
-             if (inchar () == EOF || _tolower (c) != 'a')
+             if (inchar () == EOF || TOLOWER (c) != L_('a'))
                input_error ();
              ADDW (c);
-             if (inchar () == EOF || _tolower (c) != 'n')
+             if (inchar () == EOF || TOLOWER (c) != L_('n'))
                input_error ();
              ADDW (c);
              /* It is "nan".  */
              goto scan_float;
            }
-         else if (_tolower (c) == 'i')
+         else if (TOLOWER (c) == L_('i'))
            {
              /* Maybe "inf" or "infinity".  */
              ADDW (c);
-             if (inchar () == EOF || _tolower (c) != 'n')
+             if (inchar () == EOF || TOLOWER (c) != L_('n'))
                input_error ();
              ADDW (c);
-             if (inchar () == EOF || _tolower (c) != 'f')
+             if (inchar () == EOF || TOLOWER (c) != L_('f'))
                input_error ();
              ADDW (c);
              /* It is as least "inf".  */
              if (inchar () != EOF)
                {
-                 if (_tolower (c) == 'i')
+                 if (TOLOWER (c) == L_('i'))
                    {
                      /* Now we have to read the rest as well.  */
                      ADDW (c);
-                     if (inchar () == EOF || _tolower (c) != 'n')
+                     if (inchar () == EOF || TOLOWER (c) != L_('n'))
                        input_error ();
                      ADDW (c);
-                     if (inchar () == EOF || _tolower (c) != 'i')
+                     if (inchar () == EOF || TOLOWER (c) != L_('i'))
                        input_error ();
                      ADDW (c);
-                     if (inchar () == EOF || _tolower (c) != 't')
+                     if (inchar () == EOF || TOLOWER (c) != L_('t'))
                        input_error ();
                      ADDW (c);
-                     if (inchar () == EOF || _tolower (c) != 'y')
+                     if (inchar () == EOF || TOLOWER (c) != L_('y'))
                        input_error ();
                      ADDW (c);
                    }
                  else
                    /* Never mind.  */
-                   ungetc (c, s);
+                   UNGETC (c, s);
                }
              goto scan_float;
            }
 
          is_hexa = 0;
-         exp_char = 'e';
-         if (c == '0')
+         exp_char = L_('e');
+         if (c == L_('0'))
            {
              ADDW (c);
              c = inchar ();
-             if (_tolower (c) == 'x')
+             if (TOLOWER (c) == L_('x'))
                {
                  /* It is a number in hexadecimal format.  */
                  ADDW (c);
 
                  is_hexa = 1;
-                 exp_char = 'p';
+                 exp_char = L_('p');
 
                  /* Grouping is not allowed.  */
                  flags &= ~GROUP;
@@ -1086,14 +1379,14 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
          got_dot = got_e = 0;
          do
            {
-             if (isdigit (c))
+             if (ISDIGIT (c))
                ADDW (c);
-             else if (!got_e && is_hexa && isxdigit (c))
+             else if (!got_e && is_hexa && ISXDIGIT (c))
                ADDW (c);
              else if (got_e && wp[wpsize - 1] == exp_char
-                      && (c == '-' || c == '+'))
+                      && (c == L_('-') || c == L_('+')))
                ADDW (c);
-             else if (wpsize > 0 && !got_e && _tolower (c) == exp_char)
+             else if (wpsize > 0 && !got_e && TOLOWER (c) == exp_char)
                {
                  ADDW (exp_char);
                  got_e = got_dot = 1;
@@ -1109,7 +1402,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
                {
                  /* The last read character is not part of the number
                     anymore.  */
-                 ungetc (c, s);
+                 UNGETC (c, s);
                  break;
                }
              if (width > 0)
@@ -1125,7 +1418,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 
        scan_float:
          /* Convert the number.  */
-         ADDW ('\0');
+         ADDW (L_('\0'));
          if (flags & LONGDBL)
            {
              long double d = __strtold_internal (wp, &tw, flags & GROUP);
@@ -1152,22 +1445,13 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
            ++done;
          break;
 
-       case '[':       /* Character class.  */
+       case L_('['):   /* Character class.  */
          if (flags & LONG)
-           {
-             STRING_ARG (wstr, wchar_t);
-             c = '\0';         /* This is to keep gcc quiet.  */
-           }
+           STRING_ARG (wstr, wchar_t);
          else
-           {
-             STRING_ARG (str, char);
+           STRING_ARG (str, char);
 
-             c = inchar ();
-             if (c == EOF)
-               input_error ();
-           }
-
-         if (*f == '^')
+         if (*f == L_('^'))
            {
              ++f;
              not_in = 1;
@@ -1175,6 +1459,29 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
          else
            not_in = 0;
 
+         if (width < 0)
+           /* There is no width given so there is also no limit on the
+              number of characters we read.  Therefore we set width to
+              a very high value to make the algorithm easier.  */
+           width = INT_MAX;
+
+#ifdef COMPILE_WPRINTF
+         /* Find the beginning and the end of the scanlist.  We are not
+            creating a lookup table since it would have to be too large.
+            Instead we search each time through the string.  This is not
+            a constant lookup time but who uses this feature deserves to
+            be punished.  */
+         tw = (wchar_t *) f;   /* Marks the beginning.  */
+
+         if (*f == ']' || *f == '-')
+           ++f;
+
+         while ((fc = *f++) != L'\0' && fc != L']');
+
+         if (fc == L'\0')
+           conv_error ();
+         wp = (wchar_t *) f - 1;
+#else
          /* Fill WP with byte flags indexed by character.
             We will use this flag map for matching input characters.  */
          if (wpmax < UCHAR_MAX)
@@ -1182,7 +1489,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
              wpmax = UCHAR_MAX;
              wp = (char *) alloca (wpmax);
            }
-         memset (wp, 0, UCHAR_MAX);
+         memset (wp, '\0', UCHAR_MAX);
 
          fc = *f;
          if (fc == ']' || fc == '-')
@@ -1194,85 +1501,433 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
              ++f;
            }
 
+         tw = (char *) f;
          while ((fc = *f++) != '\0' && fc != ']')
-           {
-             if (fc == '-' && *f != '\0' && *f != ']' &&
-                 (unsigned char) f[-2] <= (unsigned char) *f)
-               {
-                 /* Add all characters from the one before the '-'
-                    up to (but not including) the next format char.  */
-                 for (fc = f[-2]; fc < *f; ++fc)
-                   wp[fc] = 1;
-               }
-             else
-               /* Add the character to the flag map.  */
-               wp[fc] = 1;
-           }
+           if (fc == '-' && *f != '\0' && *f != ']' && f - 2 != tw
+               && (unsigned char) f[-2] <= (unsigned char) *f)
+             {
+               /* Add all characters from the one before the '-'
+                  up to (but not including) the next format char.  */
+               for (fc = f[-2]; fc < *f; ++fc)
+                 wp[fc] = 1;
+             }
+           else
+             /* Add the character to the flag map.  */
+             wp[fc] = 1;
+
          if (fc == '\0')
-           {
-             if (!(flags & LONG))
-               ungetc (c, s);
-             conv_error();
-           }
+           conv_error();
+#endif
 
          if (flags & LONG)
            {
-             wint_t val;
-             int first = 1;
+             size_t now = read_in;
+#ifdef COMPILE_WPRINTF
+             do
+               {
+                 wchar_t *runp;
+
+                 if (inchar () == WEOF)
+                   break;
+
+                 /* Test whether it's in the scanlist.  */
+                 runp = tw;
+                 while (runp < wp)
+                   {
+                     if (runp[0] == L'-' && runp[1] != '\0' && runp[1] != ']'
+                         && runp != tw
+                         && (unsigned int) runp[-1] <= (unsigned int) runp[1])
+                       {
+                         /* Match against all characters in between the
+                            first and last character of the sequence.  */
+                         wchar_t wc;
+
+                         for (wc = runp[-1] + 1; wc < runp[1]; ++wc)
+                           if (wc == c)
+                             break;
+
+                         if (wc == runp[1] && !not_in)
+                           break;
+                         if (wc == runp[1] && not_in)
+                           {
+                             /* The current character is not in the
+                                 scanset.  */
+                             ungetwc (c, s);
+                             goto out;
+                           }
+                       }
+                     else
+                       {
+                         if (*runp == runp[1] && !not_in)
+                           break;
+                         if (*runp != runp[1] && not_in)
+                           {
+                             ungetwc (c ,s);
+                             goto out;
+                           }
+                       }
+
+                     ++runp;
+                   }
+
+                 if (!(flags & SUPPRESS))
+                   {
+                     *wstr++ = c;
+
+                     if ((flags & MALLOC)
+                         && wstr == (wchar_t *) *strptr + strsize)
+                       {
+                         /* Enlarge the buffer.  */
+                         wstr = (wchar_t *) realloc (*strptr,
+                                                     (2 * strsize)
+                                                     * sizeof (wchar_t));
+                         if (wstr == NULL)
+                           {
+                             /* Can't allocate that much.  Last-ditch
+                                effort.  */
+                             wstr = (wchar_t *)
+                               realloc (*strptr, (strsize
+                                                  + sizeof (wchar_t)));
+                             if (wstr == NULL)
+                               {
+                                 /* We lose.  Oh well.  Terminate the string
+                                    and stop converting, so at least we don't
+                                    skip any input.  */
+                                 ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
+                                 ++done;
+                                 conv_error ();
+                               }
+                             else
+                               {
+                                 *strptr = (char *) wstr;
+                                 wstr += strsize;
+                                 ++strsize;
+                               }
+                           }
+                         else
+                           {
+                             *strptr = (char *) wstr;
+                             wstr += strsize;
+                             strsize *= 2;
+                           }
+                       }
+                   }
+               }
+             while (--width > 0);
+           out:
+#else
+             char buf[MB_LEN_MAX];
+             size_t cnt = 0;
+             mbstate_t cstate;
+
+             memset (&cstate, '\0', sizeof (cstate));
 
              do
                {
-                 size_t cnt = 0;
-                 NEXT_WIDE_CHAR (first);
-                 if (val <= 255 && wp[val] == not_in)
+               again:
+                 if (inchar () == EOF)
+                   break;
+
+                 if (wp[c] == not_in)
                    {
-                     ungetc (val, s);
+                     ungetc (c, s);
                      break;
                    }
-                 STRING_ADD_CHAR (wstr, val, wchar_t);
-                 if (width > 0)
-                   --width;
-                 first = 0;
+
+                 /* This is easy.  */
+                 if (!(flags & SUPPRESS))
+                   {
+                     size_t n;
+
+                     /* Convert it into a wide character.  */
+                     n = __mbrtowc (wstr, buf, cnt, &cstate);
+
+                     if (n == (size_t) -2)
+                       {
+                         /* Possibly correct character, just not enough
+                            input.  */
+                         assert (cnt < MB_CUR_MAX);
+                         goto again;
+                       }
+
+                     if (n != cnt)
+                       encode_error ();
+
+                     ++wstr;
+                     if ((flags & MALLOC)
+                         && wstr == (wchar_t *) *strptr + strsize)
+                       {
+                         /* Enlarge the buffer.  */
+                         wstr = (wchar_t *) realloc (*strptr,
+                                                     (2 * strsize
+                                                      * sizeof (wchar_t)));
+                         if (wstr == NULL)
+                           {
+                             /* Can't allocate that much.  Last-ditch
+                                effort.  */
+                             wstr = (wchar_t *)
+                               realloc (*strptr, ((strsize + 1)
+                                                  * sizeof (wchar_t)));
+                             if (wstr == NULL)
+                               {
+                                 /* We lose.  Oh well.  Terminate the
+                                    string and stop converting,
+                                    so at least we don't skip any input.  */
+                                 ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
+                                 ++done;
+                                 conv_error ();
+                               }
+                             else
+                               {
+                                 *strptr = (char *) wstr;
+                                 wstr += strsize;
+                                 ++strsize;
+                               }
+                           }
+                         else
+                           {
+                             *strptr = (char *) wstr;
+                             wstr += strsize;
+                             strsize *= 2;
+                           }
+                       }
+                   }
                }
-             while (width != 0);
+             while (--width > 0);
+
+             if (cnt != 0)
+               /* We stopped in the middle of recognizing another
+                  character.  That's a problem.  */
+               encode_error ();
+#endif
 
-             if (first)
+             if (now == read_in)
+               /* We haven't succesfully read any character.  */
                conv_error ();
 
              if (!(flags & SUPPRESS))
                {
-                 *wstr = L'\0';
+                 *wstr++ = L'\0';
+
+                 if ((flags & MALLOC)
+                     && wstr - (wchar_t *) *strptr != strsize)
+                   {
+                     wchar_t *cp = (wchar_t *)
+                       realloc (*strptr, ((wstr - (wchar_t *) *strptr)
+                                          * sizeof(wchar_t)));
+                     if (cp != NULL)
+                       *strptr = (char *) cp;
+                   }
+
                  ++done;
                }
            }
          else
            {
-             num.ul = read_in - 1; /* -1 because we already read one char.  */
+             size_t now = read_in;
+#ifdef COMPILE_WPRINTF
+
+             memset (&state, '\0', sizeof (state));
+
+             do
+               {
+                 wchar_t *runp;
+                 size_t n;
+
+                 if (inchar () == WEOF)
+                   break;
+
+                 /* Test whether it's in the scanlist.  */
+                 runp = tw;
+                 while (runp < wp)
+                   {
+                     if (runp[0] == L'-' && runp[1] != '\0' && runp[1] != ']'
+                         && runp != tw
+                         && (unsigned int) runp[-1] <= (unsigned int) runp[1])
+                       {
+                         /* Match against all characters in between the
+                            first and last character of the sequence.  */
+                         wchar_t wc;
+
+                         for (wc = runp[-1] + 1; wc < runp[1]; ++wc)
+                           if (wc == c)
+                             break;
+
+                         if (wc == runp[1] && !not_in)
+                           break;
+                         if (wc == runp[1] && not_in)
+                           {
+                             /* The current character is not in the
+                                 scanset.  */
+                             ungetwc (c, s);
+                             goto out2;
+                           }
+                       }
+                     else
+                       {
+                         if (*runp == runp[1] && !not_in)
+                           break;
+                         if (*runp != runp[1] && not_in)
+                           {
+                             ungetwc (c ,s);
+                             goto out2;
+                           }
+                       }
+
+                     ++runp;
+                   }
+
+                 if (!(flags & SUPPRESS))
+                   {
+                     if ((flags & MALLOC)
+                         && str + MB_CUR_MAX >= *strptr + strsize)
+                       {
+                         /* Enlarge the buffer.  */
+                         str = (char *) realloc (*strptr, 2 * strsize);
+                         if (str == NULL)
+                           {
+                             /* Can't allocate that much.  Last-ditch
+                                effort.  */
+                             str = (char *) realloc (*strptr, strsize + 1);
+                             if (str == NULL)
+                               {
+                                 /* We lose.  Oh well.  Terminate the string
+                                    and stop converting, so at least we don't
+                                    skip any input.  */
+                                 (*strptr)[strsize - 1] = '\0';
+                                 ++done;
+                                 conv_error ();
+                               }
+                             else
+                               {
+                                 *strptr = str;
+                                 str += strsize;
+                                 ++strsize;
+                               }
+                           }
+                         else
+                           {
+                             *strptr = str;
+                             str += strsize;
+                             strsize *= 2;
+                           }
+                       }
+                   }
+
+                 n = wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state);
+                 if (n == (size_t) -1)
+                   encode_error ();
+
+                 assert (n <= MB_CUR_MAX);
+                 str += n;
+               }
+             while (--width > 0);
+           out2:
+#else
              do
                {
+                 if (inchar () == EOF)
+                   break;
+
                  if (wp[c] == not_in)
                    {
                      ungetc (c, s);
                      break;
                    }
-                 STRING_ADD_CHAR (str, c, char);
-                 if (width > 0)
-                   --width;
+
+                 /* This is easy.  */
+                 if (!(flags & SUPPRESS))
+                   {
+                     *str++ = c;
+                     if ((flags & MALLOC)
+                         && (char *) str == *strptr + strsize)
+                       {
+                         /* Enlarge the buffer.  */
+                         str = (char *) realloc (*strptr, 2 * strsize);
+                         if (str == NULL)
+                           {
+                             /* Can't allocate that much.  Last-ditch
+                                effort.  */
+                             str = (char *) realloc (*strptr, strsize + 1);
+                             if (str == NULL)
+                               {
+                                 /* We lose.  Oh well.  Terminate the
+                                    string and stop converting,
+                                    so at least we don't skip any input.  */
+                                 ((char *) (*strptr))[strsize - 1] = '\0';
+                                 ++done;
+                                 conv_error ();
+                               }
+                             else
+                               {
+                                 *strptr = (char *) str;
+                                 str += strsize;
+                                 ++strsize;
+                               }
+                           }
+                         else
+                           {
+                             *strptr = (char *) str;
+                             str += strsize;
+                             strsize *= 2;
+                           }
+                       }
+                   }
                }
-             while (width != 0 && inchar () != EOF);
+             while (--width > 0);
+#endif
 
-             if (read_in == num.ul)
+             if (now == read_in)
+               /* We haven't succesfully read any character.  */
                conv_error ();
 
              if (!(flags & SUPPRESS))
                {
+#ifdef COMPILE_WPRINTF
+                 /* We have to emit the code to get into the intial
+                    state.  */
+                 char buf[MB_LEN_MAX];
+                 size_t n = wcrtomb (buf, L'\0', &state);
+                 if (n > 0 && (flags & MALLOC)
+                     && str + n >= *strptr + strsize)
+                   {
+                     /* Enlarge the buffer.  */
+                     str = (char *) realloc (*strptr,
+                                             (str + n + 1) - *strptr);
+                     if (str == NULL)
+                       {
+                         /* We lose.  Oh well.  Terminate the string
+                            and stop converting, so at least we don't
+                            skip any input.  */
+                         ((char *) (*strptr))[strsize - 1] = '\0';
+                         ++done;
+                         conv_error ();
+                       }
+                     else
+                       {
+                         *strptr = (char *) str;
+                         str = ((char *) *strptr) + strsize;
+                         strsize = (str + n + 1) - *strptr;
+                       }
+                   }
+
+                 str = __mempcpy (str, buf, n);
+#endif
                  *str = '\0';
+
+                 if ((flags & MALLOC) && str - *strptr != strsize)
+                   {
+                     char *cp = (char *) realloc (*strptr, str - *strptr);
+                     if (cp != NULL)
+                       *strptr = cp;
+                   }
+
                  ++done;
                }
            }
          break;
 
-       case 'p':       /* Generic pointer.  */
+       case L_('p'):   /* Generic pointer.  */
          base = 16;
          /* A PTR must be the same size as a `long int'.  */
          flags &= ~(SHORT|LONGDBL);
@@ -1305,11 +1960,23 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 }
 
 #ifdef USE_IN_LIBIO
+# ifdef COMPILE_WPRINTF
+int
+__vfwscanf (FILE *s, const wchar_t *format, va_list argptr)
+{
+  return _IO_vfwscanf (s, format, argptr, NULL);
+}
+# else
 int
 __vfscanf (FILE *s, const char *format, va_list argptr)
 {
   return _IO_vfscanf (s, format, argptr, NULL);
 }
+# endif
 #endif
 
+#ifdef COMPILE_WPRINTF
+weak_alias (__vfwscanf, vfwscanf)
+#else
 weak_alias (__vfscanf, vfscanf)
+#endif