about summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>1999-06-16 22:55:47 +0000
committerUlrich Drepper <drepper@redhat.com>1999-06-16 22:55:47 +0000
commitd64b6ad07585b8a37e5fecc9a47fcee766d52ede (patch)
tree076b36cc9c1b82254348212e75939d842885563a /stdio-common
parentbc938d3de936a8e429b16237180c046139be8247 (diff)
downloadglibc-d64b6ad07585b8a37e5fecc9a47fcee766d52ede.tar.gz
glibc-d64b6ad07585b8a37e5fecc9a47fcee766d52ede.tar.xz
glibc-d64b6ad07585b8a37e5fecc9a47fcee766d52ede.zip
Update.
	* Versions.def: Add GLIBC_2.2 for libc.

	* iconv/gconv.h: Make header suitable for inclusion in public header
	by protecting all names with __.
	* iconv/gconv.c: Adapt for symbol name changes.
	* iconv/gconv.h: Likewise.
	* iconv/gconv_builtin.c: Likewise.
	* iconv/gconv_close.c: Likewise.
	* iconv/gconv_db.c: Likewise.
	* iconv/gconv_dl.c: Likewise.
	* iconv/gconv_int.h: Likewise.
	* iconv/gconv_open.c: Likewise.
	* iconv/gconv_simple.c: Likewise.
	* iconv/iconv.c: Likewise.
	* iconv/iconv_close.c: Likewise.
	* iconv/iconv_open.c: Likewise.
	* iconv/loop.c: Likewise.
	* iconv/skeleton.c: Likewise.
	* iconvdata/8bit-gap.c: Likewise.
	* iconvdata/8bit-generic.c: Likewise.
	* iconvdata/ansi_x3.110.c: Likewise.
	* iconvdata/big5.c: Likewise.
	* iconvdata/cns11643.h: Likewise.
	* iconvdata/cns11643l1.h: Likewise.
	* iconvdata/euc-cn.c: Likewise.
	* iconvdata/euc-jp.c: Likewise.
	* iconvdata/euc-kr.c: Likewise.
	* iconvdata/euc-tw.c: Likewise.
	* iconvdata/gb2312.h: Likewise.
	* iconvdata/iso-2022-jp.c: Likewise.
	* iconvdata/iso-2022-kr.c: Likewise.
	* iconvdata/iso646.c: Likewise.
	* iconvdata/iso8859-1.c: Likewise.
	* iconvdata/iso_6937-2.c: Likewise.
	* iconvdata/iso_6937.c: Likewise.
	* iconvdata/jis0201.h: Likewise.
	* iconvdata/jis0208.h: Likewise.
	* iconvdata/jis0212.h: Likewise.
	* iconvdata/johab.c: Likewise.
	* iconvdata/ksc5601.h: Likewise.
	* iconvdata/sjis.c: Likewise.
	* iconvdata/t.61.c: Likewise.
	* iconvdata/uhc.c: Likewise.
	* stdlib/mblen.c: Likewise.
	* stdlib/mbtowc.c: Likewise.
	* stdlib/wctomb.c: Likewise.
	* wcsmbs/btowc.c: Likewise.
	* wcsmbs/mbrtowc.c: Likewise.
	* wcsmbs/mbsnrtowcs.c: Likewise.
	* wcsmbs/mbsrtowcs.c: Likewise.
	* wcsmbs/wchar.h: Likewise.
	* wcsmbs/wcrtomb.c: Likewise.
	* wcsmbs/wcsmbsload.c: Likewise.
	* wcsmbs/wcsmbsload.h: Likewise.
	* wcsmbs/wcsnrtombs.c: Likewise.
	* wcsmbs/wcsrtombs.c: Likewise.
	* wcsmbs/wctob.c: Likewise.

	* include/limits.h (MB_LEN_MAX): Increase to 16.

	* sysdeps/generic/_G_config.h: Define _G_fpos_t as struct.  Define
	_G_iconv_t.
	* sysdeps/unix/sysv/linux/_G_config.h: Likewise.
	* include/wchar.h: Change mbstate_t to __mbstate_t.

	* libio/Makefile (routines): Add wfiledoalloc, oldiofgetpos,
	oldiofgetpos64, oldiofsetpos, oldiofsetpos64, fputwc, fputwc_u,
	getwc, getwc_u, getwchar, getwchar_u, iofgetws, iofgetws_u,
	iofputws, iofputws_u, iogetwline, iowpadn, ioungetwc, putwc, putwc_u,
	putchar, putchar_u, swprintf, vwprintf, wprintf, wscanf, fwscanf,
	vwscanf, vswprintf, iovswscanf, swscanf, wgenops, wstrops, wfileops,
	and iofwide.
	(tests): Add tst_swprintf, tst_wprintf, tst_swscanf, and tst_wscanf.
	* libio/Versions: Add _IO_fgetpos, _IO_fgetpos64, _IO_fsetpos,
	_IO_fsetpos64, fgetpos, fgetpos64, fgetwc, fgetwc_unlocked, fgetws,
	fgetws_unlocked, fputwc, fputwc_unlocked, fputws, fputws_unlocked,
	fsetpos, fsetpos64, fwide, fwprintf, fwscanf, getwc, getwc_unlocked,
	getwchar, getwchar_unlocked, putwc, putwc_unlocked, putwchar,
	putwchar_unlocked, swprintf, swscanf, ungetwc, vfwprintf, vswprintf,
	vwprintf, vfwscanf, vswscanf, vwscanf, wprintf, and wscanf to
	GLIBC_2.2 for libc.
	* libio/libio.h: Define codecvt struct.  Define _IO_wide_data.
	Extend _IO_file contain pointer to codecvt, widedata and mode.
	(_IO_getwc_unlocked): New macro.
	(_IO_putwc_unlocked): New macro.
	(_IO_fwide): New macro.
	* libio/libioP.h: Add new prototypes and adjust existing declarations.
	* libio/fileops.c (_IO_new_file_close_it): Reset normal or widedata
	buffers based on mode.
	(new_do_write): Set _IO_write_end to _IO_buf_end if stream is wide
	oriented.
	(_IO_new_file_overflow): Don't depend only on _IO_CURRENTLY_PUTTING
	flag to be enough to signal unallocated buffer.  For wide oriented
	stream don't make it linebuffered.  Don't use _IO_do_flush, use
	_IO_new_do_write directly.
	(_IO_new_file_seekoff): Change return value type to _IO_off64_t.
	(_IO_file_seek): Likewise.
	* libio/genops.c (_IO_least_marker): Make global.
	(__underflow): Orient stream if not already done.
	(__uflow): Likewise.
	(_IO_default_seekpos): Change to type _IO_off64_t.
	(_IO_default_seekoff): Likewise.
	(_IO_default_seek): Likewise.
	(_IO_no_init): New function.  Similar to _IO_init but allows to orient
	in initialization.
	* libio/iolibio.h: Add prototype for _IO_vswprintf.  Change _IO_pos_BAD
	to use _IO_off64_t.
	* libio/ftello.c: Use _IO_off_t.  For now abort when use with wide
	char stream.
	* libio/ftello64.c: Likewise.
	* libio/ioftell.c: Likewise.
	* libio/iofopncook.c: Likewise.
	* libio/ioseekoff.c: Likewise.
	* libio/ioseekpos.c: Likewise.
	* libio/oldfileops.c: Likewise.
	* libio/iofgetpos.c: Store state of conversion if necessary.
	* libio/iofgetpos64.c: Likewise.
	* libio/iofsetpos.c: Restore conversion state if necessary.
	* libio/iofsetpos64.c: Likewise.
	* libio/iofdopen.c: Initialize so that stream can be wide oriented.
	* libio/iofopen.c: Likewise.
	* libio/iofopen64.c: Likewise.
	* libio/iopopen.c: Likewise.
	* libio/iovdprintf.c: Likewise.
	* libio/iovsprintf.c: Likewise.
	* libio/iovsscanf.c: Likewise.
	* libio/memstream.c: Likewise.
	* libio/obprintf.c: Likewise.
	* libio/iofputs.c: Orient stream if not already happened.
	* libio/iofputs_u.c: Likewise.
	* libio/iofwrite.c: Likewise.
	* libio/iofwrite_u.c: Likewise.
	* libio/ioputs.c: Likewise.
	* libio/iosetbuffer.c: Handle not yet oriented stream.
	* libio/iosetvbuf.c: Likewise.
	* libio/oldstdfiles.c: Adjust FILEBUF_LITERAL call.
	* libio/stdfiles.c: Likewise.
	* libio/strops.c (_IO_str_overflow): Correctly free buffer after
	failed allocation.
	(_IO_str_seekoff): Use _IO_off64_t.
	* libio/vasprintf.c: Pre-orient stream.
	* libio/vsnprintf.c: Likewise.
	* libio/fputwc.c: New file.
	* libio/fputwc_u.c: New file.
	* libio/fwprintf.c: New file.
	* libio/fwscanf.c: New file.
	* libio/getwc.c: New file.
	* libio/getwc_u.c: New file.
	* libio/getwchar.c: New file.
	* libio/getwchar_u.c: New file.
	* libio/iofgetws.c: New file.
	* libio/iofgetws_u.c: New file.
	* libio/iofputws.c: New file.
	* libio/iofputws_u.c: New file.
	* libio/iofwide.c: New file.
	* libio/iogetwline.c: New file.
	* libio/ioungetwc.c: New file.
	* libio/iovswscanf.c: New file.
	* libio/iowpadn.c: New file.
	* libio/oldiofgetpos.c: New file.
	* libio/oldiofgetpos64.c: New file.
	* libio/oldiofsetpos.c: New file.
	* libio/oldiofsetpos64.c: New file.
	* libio/putwc.c: New file.
	* libio/putwc_u.c: New file.
	* libio/putwchar.c: New file.
	* libio/putwchar_u.c: New file.
	* libio/swprintf.c: New file.
	* libio/swscanf.c: New file.
	* libio/tst_swprintf.c: New file.
	* libio/tst_swscanf.c: New file.
	* libio/tst_wprintf.c: New file.
	* libio/tst_wscanf.c: New file.
	* libio/tst_wscanf.input: New file.
	* libio/vswprintf.c: New file.
	* libio/vwprintf.c: New file.
	* libio/vwscanf.c: New file.
	* libio/wfiledoalloc.c: New file.
	* libio/wfileops.c: New file.
	* libio/wgenops.c: New file.
	* libio/wprintf.c: New file.
	* libio/wscanf.c: New file.
	* libio/wstrops.c: New file.
	* stdio-common/Makefile (routines): Add _itowa, itowa-digits,
	vfwprintf, and vfwscanf.
	* stdio-common/_itoa.c (base_table): Rename to _IO_base_table and
	make global.
	* stdio-common/_itowa.c: New file.
	* stdio-common/_itowa.h: New file.
	* stdio-common/itoa-digits.c: Minimal optimization.
	* stdio-common/itowa-digits.c: New file.
	* stdio-common/printf-parse.h: Allow use in wide character context.
	* stdio-common/printf-prs.c: Define ISASCII and MBRLEN.
	* stdio-common/printf.h (printf_info): Add wide bit.
	* stdio-common/printf_fp.c: Determine from wide bit whether stream
	is wide oriented or not.
	* stdio-common/printf_size.c: Likewise.
	* sysdeps/generic/printf_fphex.c: Likewise.
	* stdlib/strfmon.c: Call __printf_fp with wide bit cleared.
	* stdio-common/vfprintf.c: Rewrite to allow use in wide character
	context.
	* stdio-common/vfscand.c: Likewise.
	* stdio-common/vfwprintf.c: New file.
	* stdio-common/vfwscanf.c: New file.

	* time/Makefile (routines): Add wcsftime.
	(tests): Add tst_wcsftime.
	* time/Versions: Add wcsftime to GLIBC_2.2 for libc.
	* time/strftime.c: Make usable as wcsftime.
	* time/wcsftime.c: New file.
	* time/tst_wcsftime.c: New file.

	* wcsmbs/Makefile (routines): Add wmempcpy and wcschrnul.
	* wcsmbs/Versions: Add wmempcpy and wcschrnul to GLIBC_2.2 for libc.
	* wcsmbs/wcschrnul.c: New file.
	* wcsmbs/wmemcpy.c: New file.
	* wcsmbs/wmemcpy.c: Rename to __wmemcpy and make wmemcpy weak alias.
	* wcsmbs/wmemmove.c: Likewise for wmemmove.

	* manual/stdio.texi: Document is_char and wide element if printf_info.

	* manual/time.texi: Document wcsftime.

	* include/wchar.h: Add prototypes for __wmemcpy, __wmempcpy,
	__wmemmove, __wcschrnul, and __vfwscanf.

	* locale/langinfo.h: Add new LC_TIME entries for wchar_t data.
	* locale/C-time.c: Adapt for above change.
	* locale/categories.def: Likewise.
	* locale/localeinfo.h: Likewise.
	* localedata/Makefile: Don't run tests for now.
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/Makefile3
-rw-r--r--stdio-common/_itoa.c4
-rw-r--r--stdio-common/_itowa.c346
-rw-r--r--stdio-common/_itowa.h63
-rw-r--r--stdio-common/itoa-digits.c7
-rw-r--r--stdio-common/itowa-digits.c27
-rw-r--r--stdio-common/printf-parse.h71
-rw-r--r--stdio-common/printf-prs.c4
-rw-r--r--stdio-common/printf.h3
-rw-r--r--stdio-common/printf_fp.c13
-rw-r--r--stdio-common/printf_size.c3
-rw-r--r--stdio-common/vfprintf.c585
-rw-r--r--stdio-common/vfscanf.c1313
-rw-r--r--stdio-common/vfwprintf.c3
-rw-r--r--stdio-common/vfwscanf.c2
15 files changed, 1874 insertions, 573 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 9fb0c5d15d..74900189a8 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -25,11 +25,12 @@ headers	:= printf.h
 
 routines	:=							      \
 	ctermid cuserid							      \
-	_itoa itoa-digits						      \
+	_itoa _itowa itoa-digits itowa-digits				      \
 	vfprintf vprintf printf_fp reg-printf printf-prs printf_fphex	      \
 	printf_size fprintf printf snprintf sprintf asprintf dprintf	      \
 	vfscanf 							      \
 	fscanf scanf sscanf						      \
+	vfwprintf vfwscanf						      \
 	perror psignal							      \
 	tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname		      \
 	getline getw putw						      \
diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c
index 3a7cd78003..2eca838229 100644
--- a/stdio-common/_itoa.c
+++ b/stdio-common/_itoa.c
@@ -78,7 +78,7 @@ struct base_table_t
 
 
 /* Local variables.  */
-static const struct base_table_t base_table[] =
+const struct base_table_t _itoa_base_table[] =
 {
 #if BITS_PER_MP_LIMB == 64
   /*  2 */ {SEL1(0ul) 1, 1},
@@ -171,7 +171,7 @@ _itoa (value, buflim, base, upper_case)
 {
   const char *digits = upper_case ? _itoa_upper_digits : _itoa_lower_digits;
   char *bp = buflim;
-  const struct base_table_t *brec = &base_table[base - 2];
+  const struct base_table_t *brec = &_itoa_base_table[base - 2];
 
   switch (base)
     {
diff --git a/stdio-common/_itowa.c b/stdio-common/_itowa.c
new file mode 100644
index 0000000000..430415b96b
--- /dev/null
+++ b/stdio-common/_itowa.c
@@ -0,0 +1,346 @@
+/* Internal function for converting integers to ASCII.
+   Copyright (C) 1994, 1995, 1996, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Torbjorn Granlund <tege@matematik.su.se>
+   and Ulrich Drepper <drepper@gnu.org>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <gmp-mparam.h>
+#include <stdlib/gmp.h>
+#include <stdlib/gmp-impl.h>
+#include <stdlib/longlong.h>
+
+#include "_itowa.h"
+
+
+/* Canonize environment.  For some architectures not all values might
+   be defined in the GMP header files.  */
+#ifndef UMUL_TIME
+# define UMUL_TIME 1
+#endif
+#ifndef UDIV_TIME
+# define UDIV_TIME 3
+#endif
+
+/* Control memory layout.  */
+#ifdef PACK
+# undef PACK
+# define PACK __attribute__ ((packed))
+#else
+# define PACK
+#endif
+
+
+/* Declare local types.  */
+struct base_table_t
+{
+#if (UDIV_TIME > 2 * UMUL_TIME)
+  mp_limb_t base_multiplier;
+#endif
+  char flag;
+  char post_shift;
+#if BITS_PER_MP_LIMB == 32
+  struct
+    {
+      char normalization_steps;
+      char ndigits;
+      mp_limb_t base PACK;
+#if UDIV_TIME > 2 * UMUL_TIME
+      mp_limb_t base_ninv PACK;
+#endif
+    } big;
+#endif
+};
+
+/* To reduce the memory needed we include some fields of the tables
+   only conditionally.  */
+#if UDIV_TIME > 2 * UMUL_TIME
+# define SEL1(X) X,
+# define SEL2(X) ,X
+#else
+# define SEL1(X)
+# define SEL2(X)
+#endif
+
+/* Factor table for the different bases.  */
+extern const struct base_table_t _itoa_base_table[];
+
+/* Lower-case digits.  */
+extern const wchar_t _itowa_lower_digits[];
+/* Upper-case digits.  */
+extern const wchar_t _itowa_upper_digits[];
+
+
+wchar_t *
+_itowa (value, buflim, base, upper_case)
+     unsigned long long int value;
+     wchar_t *buflim;
+     unsigned int base;
+     int upper_case;
+{
+  const wchar_t *digits = (upper_case
+			   ? _itowa_upper_digits : _itowa_lower_digits);
+  wchar_t *bp = buflim;
+  const struct base_table_t *brec = &_itoa_base_table[base - 2];
+
+  switch (base)
+    {
+#define RUN_2N(BITS) \
+      do								      \
+        {								      \
+	  /* `unsigned long long int' always has 64 bits.  */		      \
+	  mp_limb_t work_hi = value >> (64 - BITS_PER_MP_LIMB);		      \
+									      \
+	  if (BITS_PER_MP_LIMB == 32)					      \
+	    {								      \
+	      if (work_hi != 0)						      \
+		{							      \
+		  mp_limb_t work_lo;					      \
+		  int cnt;						      \
+									      \
+		  work_lo = value & 0xfffffffful;			      \
+		  for (cnt = BITS_PER_MP_LIMB / BITS; cnt > 0; --cnt)	      \
+		    {							      \
+		      *--bp = digits[work_lo & ((1ul << BITS) - 1)];	      \
+		      work_lo >>= BITS;					      \
+		    }							      \
+		  if (BITS_PER_MP_LIMB % BITS != 0)			      \
+		    {							      \
+		      work_lo						      \
+			|= ((work_hi					      \
+			     & ((1 << (BITS - BITS_PER_MP_LIMB%BITS))	      \
+				- 1))					      \
+			    << BITS_PER_MP_LIMB % BITS);		      \
+		      work_hi >>= BITS - BITS_PER_MP_LIMB % BITS;	      \
+		      if (work_hi == 0)					      \
+			work_hi = work_lo;				      \
+		      else						      \
+			*--bp = digits[work_lo];			      \
+		    }							      \
+		}							      \
+	      else							      \
+		work_hi = value & 0xfffffffful;				      \
+	    }								      \
+	  do								      \
+	    {								      \
+	      *--bp = digits[work_hi & ((1 << BITS) - 1)];		      \
+	      work_hi >>= BITS;						      \
+	    }								      \
+	  while (work_hi != 0);						      \
+	}								      \
+      while (0)
+    case 8:
+      RUN_2N (3);
+      break;
+
+    case 16:
+      RUN_2N (4);
+      break;
+
+    default:
+      {
+#if BITS_PER_MP_LIMB == 64
+	mp_limb_t base_multiplier = brec->base_multiplier;
+	if (brec->flag)
+	  while (value != 0)
+	    {
+	      mp_limb_t quo, rem, x, dummy;
+
+	      umul_ppmm (x, dummy, value, base_multiplier);
+	      quo = (x + ((value - x) >> 1)) >> (brec->post_shift - 1);
+	      rem = value - quo * base;
+	      *--bp = digits[rem];
+	      value = quo;
+	    }
+	else
+	  while (value != 0)
+	    {
+	      mp_limb_t quo, rem, x, dummy;
+
+	      umul_ppmm (x, dummy, value, base_multiplier);
+	      quo = x >> brec->post_shift;
+	      rem = value - quo * base;
+	      *--bp = digits[rem];
+	      value = quo;
+	    }
+#endif
+#if BITS_PER_MP_LIMB == 32
+	mp_limb_t t[3];
+	int n;
+
+	/* First convert x0 to 1-3 words in base s->big.base.
+	   Optimize for frequent cases of 32 bit numbers.  */
+	if ((mp_limb_t) (value >> 32) >= 1)
+	  {
+#if UDIV_TIME > 2 * UMUL_TIME || UDIV_NEEDS_NORMALIZATION
+	    int big_normalization_steps = brec->big.normalization_steps;
+	    mp_limb_t big_base_norm
+	      = brec->big.base << big_normalization_steps;
+#endif
+	    if ((mp_limb_t) (value >> 32) >= brec->big.base)
+	      {
+		mp_limb_t x1hi, x1lo, r;
+		/* If you want to optimize this, take advantage of
+		   that the quotient in the first udiv_qrnnd will
+		   always be very small.  It might be faster just to
+		   subtract in a tight loop.  */
+
+#if UDIV_TIME > 2 * UMUL_TIME
+		mp_limb_t x, xh, xl;
+
+		if (big_normalization_steps == 0)
+		  xh = 0;
+		else
+		  xh = (mp_limb_t) (value >> (64 - big_normalization_steps));
+		xl = (mp_limb_t) (value >> (32 - big_normalization_steps));
+		udiv_qrnnd_preinv (x1hi, r, xh, xl, big_base_norm,
+				   brec->big.base_ninv);
+
+		xl = ((mp_limb_t) value) << big_normalization_steps;
+		udiv_qrnnd_preinv (x1lo, x, r, xl, big_base_norm,
+				   brec->big.base_ninv);
+		t[2] = x >> big_normalization_steps;
+
+		if (big_normalization_steps == 0)
+		  xh = x1hi;
+		else
+		  xh = ((x1hi << big_normalization_steps)
+			| (x1lo >> (32 - big_normalization_steps)));
+		xl = x1lo << big_normalization_steps;
+		udiv_qrnnd_preinv (t[0], x, xh, xl, big_base_norm,
+				   brec->big.base_ninv);
+		t[1] = x >> big_normalization_steps;
+#elif UDIV_NEEDS_NORMALIZATION
+		mp_limb_t x, xh, xl;
+
+		if (big_normalization_steps == 0)
+		  xh = 0;
+		else
+		  xh = (mp_limb_t) (value >> 64 - big_normalization_steps);
+		xl = (mp_limb_t) (value >> 32 - big_normalization_steps);
+		udiv_qrnnd (x1hi, r, xh, xl, big_base_norm);
+
+		xl = ((mp_limb_t) value) << big_normalization_steps;
+		udiv_qrnnd (x1lo, x, r, xl, big_base_norm);
+		t[2] = x >> big_normalization_steps;
+
+		if (big_normalization_steps == 0)
+		  xh = x1hi;
+		else
+		  xh = ((x1hi << big_normalization_steps)
+			| (x1lo >> 32 - big_normalization_steps));
+		xl = x1lo << big_normalization_steps;
+		udiv_qrnnd (t[0], x, xh, xl, big_base_norm);
+		t[1] = x >> big_normalization_steps;
+#else
+		udiv_qrnnd (x1hi, r, 0, (mp_limb_t) (value >> 32),
+			    brec->big.base);
+		udiv_qrnnd (x1lo, t[2], r, (mp_limb_t) value, brec->big.base);
+		udiv_qrnnd (t[0], t[1], x1hi, x1lo, brec->big.base);
+#endif
+		n = 3;
+	      }
+	    else
+	      {
+#if (UDIV_TIME > 2 * UMUL_TIME)
+		mp_limb_t x;
+
+		value <<= brec->big.normalization_steps;
+		udiv_qrnnd_preinv (t[0], x, (mp_limb_t) (value >> 32),
+				   (mp_limb_t) value, big_base_norm,
+				   brec->big.base_ninv);
+		t[1] = x >> brec->big.normalization_steps;
+#elif UDIV_NEEDS_NORMALIZATION
+		mp_limb_t x;
+
+		value <<= big_normalization_steps;
+		udiv_qrnnd (t[0], x, (mp_limb_t) (value >> 32),
+			    (mp_limb_t) value, big_base_norm);
+		t[1] = x >> big_normalization_steps;
+#else
+		udiv_qrnnd (t[0], t[1], (mp_limb_t) (value >> 32),
+			    (mp_limb_t) value, brec->big.base);
+#endif
+		n = 2;
+	      }
+	  }
+	else
+	  {
+	    t[0] = value;
+	    n = 1;
+	  }
+
+	/* Convert the 1-3 words in t[], word by word, to ASCII.  */
+	do
+	  {
+	    mp_limb_t ti = t[--n];
+	    int ndig_for_this_limb = 0;
+
+#if UDIV_TIME > 2 * UMUL_TIME
+	    mp_limb_t base_multiplier = brec->base_multiplier;
+	    if (brec->flag)
+	      while (ti != 0)
+		{
+		  mp_limb_t quo, rem, x, dummy;
+
+		  umul_ppmm (x, dummy, ti, base_multiplier);
+		  quo = (x + ((ti - x) >> 1)) >> (brec->post_shift - 1);
+		  rem = ti - quo * base;
+		  *--bp = digits[rem];
+		  ti = quo;
+		  ++ndig_for_this_limb;
+		}
+	    else
+	      while (ti != 0)
+		{
+		  mp_limb_t quo, rem, x, dummy;
+
+		  umul_ppmm (x, dummy, ti, base_multiplier);
+		  quo = x >> brec->post_shift;
+		  rem = ti - quo * base;
+		  *--bp = digits[rem];
+		  ti = quo;
+		  ++ndig_for_this_limb;
+		}
+#else
+	    while (ti != 0)
+	      {
+		mp_limb_t quo, rem;
+
+		quo = ti / base;
+		rem = ti % base;
+		*--bp = digits[rem];
+		ti = quo;
+		++ndig_for_this_limb;
+	      }
+#endif
+	    /* If this wasn't the most significant word, pad with zeros.  */
+	    if (n != 0)
+	      while (ndig_for_this_limb < brec->big.ndigits)
+		{
+		  *--bp = '0';
+		  ++ndig_for_this_limb;
+		}
+	  }
+	while (n != 0);
+#endif
+      }
+      break;
+    }
+
+  return bp;
+}
diff --git a/stdio-common/_itowa.h b/stdio-common/_itowa.h
new file mode 100644
index 0000000000..e219f298ee
--- /dev/null
+++ b/stdio-common/_itowa.h
@@ -0,0 +1,63 @@
+/* Internal function for converting integers to ASCII.
+   Copyright (C) 1994, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _ITOWA_H
+#define _ITOWA_H	1
+#include <features.h>
+#include <wchar.h>
+
+/* Convert VALUE into ASCII in base BASE (2..36).
+   Write backwards starting the character just before BUFLIM.
+   Return the address of the first (left-to-right) character in the number.
+   Use upper case letters iff UPPER_CASE is nonzero.  */
+
+extern wchar_t *_itowa __P ((unsigned long long int value, wchar_t *buflim,
+			     unsigned int base, int upper_case));
+
+static inline wchar_t *
+__attribute__ ((unused))
+_itowa_word (unsigned long value, wchar_t *buflim,
+	     unsigned int base, int upper_case)
+{
+  extern const wchar_t _itowa_upper_digits[], _itowa_lower_digits[];
+  const wchar_t *digits = (upper_case
+			   ? _itowa_upper_digits : _itowa_lower_digits);
+  wchar_t *bp = buflim;
+
+  switch (base)
+    {
+#define SPECIAL(Base)							      \
+    case Base:								      \
+      do								      \
+	*--bp = digits[value % Base];					      \
+      while ((value /= Base) != 0);					      \
+      break
+
+      SPECIAL (10);
+      SPECIAL (16);
+      SPECIAL (8);
+    default:
+      do
+	*--bp = digits[value % base];
+      while ((value /= base) != 0);
+    }
+  return bp;
+}
+
+#endif	/* itowa.h */
diff --git a/stdio-common/itoa-digits.c b/stdio-common/itoa-digits.c
index b475bbca42..34699dbcc8 100644
--- a/stdio-common/itoa-digits.c
+++ b/stdio-common/itoa-digits.c
@@ -1,5 +1,5 @@
 /* Digits.
-   Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1994, 1995, 1996, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -18,9 +18,8 @@
    Boston, MA 02111-1307, USA.  */
 
 /* Lower-case digits.  */
-const char _itoa_lower_digits[]
+const char _itoa_lower_digits[36]
 	= "0123456789abcdefghijklmnopqrstuvwxyz";
 /* Upper-case digits.  */
-const char _itoa_upper_digits[]
+const char _itoa_upper_digits[36]
 	= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
diff --git a/stdio-common/itowa-digits.c b/stdio-common/itowa-digits.c
new file mode 100644
index 0000000000..60a85789e3
--- /dev/null
+++ b/stdio-common/itowa-digits.c
@@ -0,0 +1,27 @@
+/* Digits.
+   Copyright (C) 1994, 1995, 1996, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <wchar.h>
+
+/* Lower-case digits.  */
+const wchar_t _itowa_lower_digits[36]
+	= L"0123456789abcdefghijklmnopqrstuvwxyz";
+/* Upper-case digits.  */
+const wchar_t _itowa_upper_digits[36]
+	= L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
diff --git a/stdio-common/printf-parse.h b/stdio-common/printf-parse.h
index a915f03f18..d62f3a835e 100644
--- a/stdio-common/printf-parse.h
+++ b/stdio-common/printf-parse.h
@@ -35,7 +35,7 @@ struct printf_spec
 
     /* Pointers into the format string for the end of this format
        spec and the next (or to the end of the string if no more).  */
-    const char *end_of_fmt, *next_fmt;
+    const UCHAR_T *end_of_fmt, *next_fmt;
 
     /* Position of arguments for precision and width, or -1 if `info' has
        the constant value.  */
@@ -90,21 +90,29 @@ read_int (const UCHAR_T * *pstr)
 
 /* Find the next spec in FORMAT, or the end of the string.  Returns
    a pointer into FORMAT, to a '%' or a '\0'.  */
-static inline const char *
-find_spec (const char *format, mbstate_t *ps)
+static inline const UCHAR_T *
+#ifdef COMPILE_WPRINTF
+find_spec (const UCHAR_T *format)
+#else
+find_spec (const UCHAR_T *format, mbstate_t *ps)
+#endif
 {
-  while (*format != '\0' && *format != '%')
+#ifdef COMPILE_WPRINTF
+  return (const UCHAR_T *) __wcschrnul ((const CHAR_T *) format, L'%');
+#else
+  while (*format != L_('\0') && *format != L_('%'))
     {
       int len;
 
       /* Remove any hints of a wrong encoding.  */
       ps->count = 0;
-      if (isascii (*format) || (len = mbrlen (format, MB_CUR_MAX, ps)) <= 0)
-	++format;
-      else
+      if (! ISASCII (*format) && (len = MBRLEN (format, MB_CUR_MAX, ps)) > 0)
 	format += len;
+      else
+	++format;
     }
   return format;
+#endif
 }
 
 
@@ -119,8 +127,13 @@ extern printf_function **__printf_function_table;
    the number of args consumed by this spec; *MAX_REF_ARG is updated so it
    remains the highest argument index used.  */
 static inline size_t
+#ifdef COMPILE_WPRINTF
+parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec,
+		size_t *max_ref_arg)
+#else
 parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec,
 		size_t *max_ref_arg, mbstate_t *ps)
+#endif
 {
   unsigned int n;
   size_t nargs = 0;
@@ -342,12 +355,12 @@ parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec,
 
       switch (spec->info.spec)
 	{
-	case L'i':
-	case L'd':
-	case L'u':
-	case L'o':
-	case L'X':
-	case L'x':
+	case L_('i'):
+	case L_('d'):
+	case L_('u'):
+	case L_('o'):
+	case L_('X'):
+	case L_('x'):
 #if LONG_MAX != LONG_LONG_MAX
 	  if (spec->info.is_long_double)
 	    spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG;
@@ -362,38 +375,38 @@ parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec,
 	    else
 	      spec->data_arg_type = PA_INT;
 	  break;
-	case L'e':
-	case L'E':
-	case L'f':
-	case L'g':
-	case L'G':
-	case L'a':
-	case L'A':
+	case L_('e'):
+	case L_('E'):
+	case L_('f'):
+	case L_('g'):
+	case L_('G'):
+	case L_('a'):
+	case L_('A'):
 	  if (spec->info.is_long_double)
 	    spec->data_arg_type = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
 	  else
 	    spec->data_arg_type = PA_DOUBLE;
 	  break;
-	case L'c':
+	case L_('c'):
 	  spec->data_arg_type = PA_CHAR;
 	  break;
-	case L'C':
+	case L_('C'):
 	  spec->data_arg_type = PA_WCHAR;
 	  break;
-	case L's':
+	case L_('s'):
 	  spec->data_arg_type = PA_STRING;
 	  break;
-	case L'S':
+	case L_('S'):
 	  spec->data_arg_type = PA_WSTRING;
 	  break;
-	case L'p':
+	case L_('p'):
 	  spec->data_arg_type = PA_POINTER;
 	  break;
-	case L'n':
+	case L_('n'):
 	  spec->data_arg_type = PA_INT|PA_FLAG_PTR;
 	  break;
 
-	case L'm':
+	case L_('m'):
 	default:
 	  /* An unknown spec will consume no args.  */
 	  spec->ndata_args = 0;
@@ -416,7 +429,11 @@ parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec,
     {
       /* Find the next format spec.  */
       spec->end_of_fmt = format;
+#ifdef COMPILE_WPRINTF
+      spec->next_fmt = find_spec (format);
+#else
       spec->next_fmt = find_spec (format, ps);
+#endif
     }
 
   return nargs;
diff --git a/stdio-common/printf-prs.c b/stdio-common/printf-prs.c
index 4f15373544..19869cad19 100644
--- a/stdio-common/printf-prs.c
+++ b/stdio-common/printf-prs.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1992, 1995, 1996 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1992, 1995, 1996, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -29,6 +29,8 @@
 # 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)
 
 # ifdef USE_IN_LIBIO
 #  define PUT(F, S, N)	_IO_sputn (F, S, N)
diff --git a/stdio-common/printf.h b/stdio-common/printf.h
index 18b2f4ab8a..66ac5d0742 100644
--- a/stdio-common/printf.h
+++ b/stdio-common/printf.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 92, 93, 95, 96, 97, 98 Free Software Foundation, Inc.
+/* Copyright (C) 1991,92,93,95,96,97,98,99 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -45,6 +45,7 @@ struct printf_info
   unsigned int group:1;		/* ' flag.  */
   unsigned int extra:1;		/* For special use.  */
   unsigned int is_char:1;	/* hh flag.  */
+  unsigned int wide:1;		/* Nonzero for wide character streams.  */
   wchar_t pad;			/* Padding character.  */
 };
 
diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c
index c75289a3a9..1b550abe4f 100644
--- a/stdio-common/printf_fp.c
+++ b/stdio-common/printf_fp.c
@@ -52,11 +52,12 @@
    the GNU I/O library.	 */
 #ifdef USE_IN_LIBIO
 #  define PUT(f, s, n) _IO_sputn (f, s, n)
-#  define PAD(f, c, n) _IO_padn (f, c, n)
+#  define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
 /* We use this file GNU C library and GNU I/O library.	So make
    names equal.	 */
 #  undef putc
-#  define putc(c, f) _IO_putc_unlocked (c, f)
+#  define putc(c, f) (wide \
+		      ? _IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
 #  define size_t     _IO_size_t
 #  define FILE	     _IO_FILE
 #else	/* ! USE_IN_LIBIO */
@@ -188,6 +189,9 @@ __printf_fp (FILE *fp,
   /* General helper (carry limb).  */
   mp_limb_t cy;
 
+  /* Nonzero if this is output on a wide character stream.  */
+  int wide = info->wide;
+
   char hack_digit (void)
     {
       mp_limb_t hi;
@@ -765,7 +769,10 @@ __printf_fp (FILE *fp,
 	if ((expsign == 0 && exponent >= dig_max)
 	    || (expsign != 0 && exponent > 4))
 	  {
-	    type = isupper (info->spec) ? 'E' : 'e';
+	    if ('g' - 'G' == 'e' - 'E')
+	      type = 'E' + (info->spec - 'G');
+	    else
+	      type = isupper (info->spec) ? 'E' : 'e';
 	    fracdig_max = dig_max - 1;
 	    intdig_max = 1;
 	    chars_needed = 1 + 1 + fracdig_max + 1 + 1 + 4;
diff --git a/stdio-common/printf_size.c b/stdio-common/printf_size.c
index 34581067dc..654675a0d7 100644
--- a/stdio-common/printf_size.c
+++ b/stdio-common/printf_size.c
@@ -1,5 +1,5 @@
 /* Print size value using units for orders of magnitude.
-   Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
    Based on a proposal by Larry McVoy <lm@sgi.com>.
@@ -212,6 +212,7 @@ printf_size (FILE *fp, const struct printf_info *info, const void *const *args)
   fp_info.group = info->group;
   fp_info.extra = info->extra;
   fp_info.pad = info->pad;
+  fp_info.wide = 0;
 
   if (fp_info.left && fp_info.pad == L' ')
     {
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 390ce91f71..fe145d6a3d 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -36,74 +36,16 @@
    Beside this it is also shared between the normal and wide character
    implementation as defined in ISO/IEC 9899:1990/Amendment 1:1995.  */
 
-#ifndef COMPILE_WPRINTF
-# define CHAR_T		char
-# define UCHAR_T	unsigned char
-# define INT_T		int
-# define L_(Str)	Str
-# define ISDIGIT(Ch)	isdigit (Ch)
-
-# ifdef USE_IN_LIBIO
-#  define PUT(F, S, N)	_IO_sputn ((F), (S), (N))
-#  define PAD(Padchar)							      \
-  if (width > 0)							      \
-    done += _IO_padn (s, (Padchar), width)
-# else
-#  define PUTC(C, F)	putc (C, F)
-ssize_t __printf_pad __P ((FILE *, char pad, size_t n));
-# define PAD(Padchar)							      \
-  if (width > 0)							      \
-    { ssize_t __res = __printf_pad (s, (Padchar), width);		      \
-      if (__res == -1)							      \
-	{								      \
-	  done = -1;							      \
-	  goto all_done;						      \
-	}								      \
-      done += __res; }
-# endif
-#else
-# define vfprintf	vfwprintf
-# define CHAR_T		wchar_t
-# define UCHAR_T	uwchar_t
-# define INT_T		wint_t
-# define L_(Str)	L##Str
-# define ISDIGIT(Ch)	iswdigit (Ch)
-
-# ifdef USE_IN_LIBIO
-#  define PUT(F, S, N)	_IO_sputn ((F), (S), (N))
-#  define PAD(Padchar)							      \
-  if (width > 0)							      \
-    done += _IO_wpadn (s, (Padchar), width)
-# else
-#  define PUTC(C, F)	wputc (C, F)
-ssize_t __wprintf_pad __P ((FILE *, wchar_t pad, size_t n));
-#  define PAD(Padchar)							      \
-  if (width > 0)							      \
-    { ssize_t __res = __wprintf_pad (s, (Padchar), width);		      \
-      if (__res == -1)							      \
-	{								      \
-	  done = -1;							      \
-	  goto all_done;						      \
-	}								      \
-      done += __res; }
-# endif
-#endif
-
-/* Include the shared code for parsing the format string.  */
-#include "printf-parse.h"
-
 
 #ifdef USE_IN_LIBIO
 /* This code is for use in libio.  */
 # include <libioP.h>
-# define PUTC(C, F)	_IO_putc_unlocked (C, F)
-# define vfprintf	_IO_vfprintf
 # define FILE		_IO_FILE
 # undef va_list
 # define va_list	_IO_va_list
-# undef	BUFSIZ
+# undef BUFSIZ
 # define BUFSIZ		_IO_BUFSIZ
-# define ARGCHECK(S, Format)						      \
+# define ARGCHECK(S, Format) \
   do									      \
     {									      \
       /* Check file argument for consistence.  */			      \
@@ -120,11 +62,54 @@ ssize_t __wprintf_pad __P ((FILE *, wchar_t pad, size_t n));
 	}								      \
     } while (0)
 # define UNBUFFERED_P(S) ((S)->_IO_file_flags & _IO_UNBUFFERED)
+
+# ifndef COMPILE_WPRINTF
+#  define vfprintf	_IO_vfprintf
+#  define CHAR_T	char
+#  define UCHAR_T	unsigned char
+#  define INT_T		int
+#  define L_(Str)	Str
+#  define ISDIGIT(Ch)	isdigit (Ch)
+#  define ISASCII(Ch)	isascii (Ch)
+#  define MBRLEN(Cp, L, St) mbrlen (Cp, L, St)
+
+#  define PUT(F, S, N)	_IO_sputn ((F), (S), (N))
+#  define PAD(Padchar) \
+  if (width > 0)							      \
+    done += _IO_padn (s, (Padchar), width)
+#  define PUTC(C, F)	_IO_putc_unlocked (C, F)
+#  define ORIENT	if (_IO_fwide (s, -1) != -1) return -1
+# else
+# include "_itowa.h"
+
+#  define vfprintf	_IO_vfwprintf
+#  define CHAR_T	wchar_t
+/* This is a hack!!!  There should be a type uwchar_t.  */
+#  define UCHAR_T	unsigned int /* uwchar_t */
+#  define INT_T		wint_t
+#  define L_(Str)	L##Str
+#  define ISDIGIT(Ch)	iswdigit (Ch)
+#  define ISASCII(Ch)	(((unsigned int) (Ch) & ~0x7f) == 0)
+#  define MBRLEN(Cp, L, St) wcslen ((const wchar_t *) (Cp))
+
+#  define PUT(F, S, N)	_IO_sputn ((F), (S), (N))
+#  define PAD(Padchar) \
+  if (width > 0)							      \
+    done += _IO_wpadn (s, (Padchar), width)
+#  define PUTC(C, F)	_IO_putwc_unlocked (C, F)
+#  define ORIENT	if (_IO_fwide (s, 1) != 1) return -1
+
+#  define _itoa(Val, Buf, Base, Case) _itowa (Val, (wchar_t *) Buf, Base, Case)
+#  define _itoa_word(Val, Buf, Base, Case) _itowa_word (Val, (wchar_t *) Buf, \
+							Base, Case)
+#  undef EOF
+#  define EOF WEOF
+# endif
 #else /* ! USE_IN_LIBIO */
 /* This code is for use in the GNU C library.  */
 # include <stdio.h>
 # define PUT(F, S, N)	fwrite (S, 1, N, F)
-# define ARGCHECK(S, Format)						      \
+# define ARGCHECK(S, Format) \
   do									      \
     {									      \
       /* Check file argument for consistence.  */			      \
@@ -153,11 +138,14 @@ extern void __flockfile (FILE *);
 extern void __funlockfile (FILE *);
 #endif /* USE_IN_LIBIO */
 
+/* Include the shared code for parsing the format string.  */
+#include "printf-parse.h"
+
 
 #define	outchar(Ch)							      \
   do									      \
     {									      \
-      register const int outc = (Ch);					      \
+      register const INT_T outc = (Ch);					      \
       if (PUTC (outc, s) == EOF)					      \
 	{								      \
 	  done = -1;							      \
@@ -199,7 +187,7 @@ extern void __funlockfile (FILE *);
 
 
 /* Global variables.  */
-static const char null[] = "(null)";
+static const CHAR_T null[] = L_("(null)");
 
 
 /* Helper function to provide temporary buffering for unbuffered streams.  */
@@ -211,7 +199,8 @@ static int printf_unknown __P ((FILE *, const struct printf_info *,
 				const void *const *));
 
 /* Group digits of number string.  */
-static char *group_number __P ((CHAR_T *, CHAR_T *, const CHAR_T *, wchar_t))
+static UCHAR_T *group_number __P ((UCHAR_T *, UCHAR_T *, const char *,
+				   wchar_t))
      internal_function;
 
 
@@ -238,11 +227,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
   const UCHAR_T *end_of_spec;
 
   /* Buffer intermediate results.  */
-  char work_buffer[1000];
-  char *workend;
+  UCHAR_T work_buffer[1000];
+  UCHAR_T *workend;
 
   /* State for restartable multibyte character handling functions.  */
+#ifndef COMPILE_WPRINTF
   mbstate_t mbstate;
+#endif
 
   /* We have to save the original argument pointer.  */
   va_list ap_save;
@@ -505,7 +496,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	 now process the wanted format specifier.  */			      \
     LABEL (form_percent):						      \
       /* Write a literal "%".  */					      \
-      outchar ('%');							      \
+      outchar (L_('%'));						      \
       break;								      \
 									      \
     LABEL (form_integer):						      \
@@ -588,7 +579,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	  else								      \
 	    /* We have to take care for the '0' flag.  If a precision	      \
 	       is given it must be ignored.  */				      \
-	    pad = ' ';							      \
+	    pad = L_(' ');						      \
 									      \
 	  /* If the precision is 0 and the number is 0 nothing has to	      \
 	     be written for the number, except for the 'o' format in	      \
@@ -597,13 +588,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    {								      \
 	      string = workend;						      \
 	      if (base == 8 && alt)					      \
-		*string-- = '0';					      \
+		*string-- = L_('0');					      \
 	    }								      \
 	  else								      \
 	    {								      \
 	      /* Put the number in WORK.  */				      \
-	      string = _itoa (number.longlong, workend + 1, base,	      \
-			      spec == 'X');				      \
+	      string = (UCHAR_T *) _itoa (number.longlong, workend + 1, base, \
+					  spec == L_('X'));		      \
 	      string -= 1;						      \
 	      if (group && grouping)					      \
 		string = group_number (string, workend, grouping,	      \
@@ -642,7 +633,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	  else								      \
 	    /* We have to take care for the '0' flag.  If a precision	      \
 	       is given it must be ignored.  */				      \
-	    pad = ' ';							      \
+	    pad = L_(' ');						      \
 									      \
 	  /* If the precision is 0 and the number is 0 nothing has to	      \
 	     be written for the number, except for the 'o' format in	      \
@@ -651,13 +642,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    {								      \
 	      string = workend;						      \
 	      if (base == 8 && alt)					      \
-		*string-- = '0';					      \
+		*string-- = L_('0');					      \
 	    }								      \
 	  else								      \
 	    {								      \
 	      /* Put the number in WORK.  */				      \
-	      string = _itoa_word (number.word, workend + 1, base,	      \
-				   spec == 'X');			      \
+	      string = (UCHAR_T *) _itoa_word (number.word, workend + 1,      \
+					       base, spec == L_('X'));	      \
 	      string -= 1;						      \
 	      if (group && grouping)					      \
 		string = group_number (string, workend, grouping,	      \
@@ -670,10 +661,10 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       if (prec > 0)							      \
 	/* Add zeros to the precision.  */				      \
 	while (prec-- > 0)						      \
-	  *string-- = '0';						      \
+	  *string-- = L_('0');						      \
       else if (number.word != 0 && alt && base == 8)			      \
 	/* Add octal marker.  */					      \
-	*string-- = '0';						      \
+	*string-- = L_('0');						      \
 									      \
       if (!left)							      \
 	{								      \
@@ -686,41 +677,41 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	  if (is_negative || showsign || space)				      \
 	    --width;							      \
 									      \
-	  if (pad == '0')						      \
+	  if (pad == L_('0'))						      \
 	    {								      \
 	      while (width-- > 0)					      \
-		*string-- = '0';					      \
+		*string-- = L_('0');					      \
 									      \
 	      if (number.word != 0 && alt && base == 16)		      \
 		{							      \
 		  *string-- = spec;					      \
-		  *string-- = '0';					      \
+		  *string-- = L_('0');					      \
 		}							      \
 									      \
 	      if (is_negative)						      \
-		*string-- = '-';					      \
+		*string-- = L_('-');					      \
 	      else if (showsign)					      \
-		*string-- = '+';					      \
+		*string-- = L_('+');					      \
 	      else if (space)						      \
-		*string-- = ' ';					      \
+		*string-- = L_(' ');					      \
 	    }								      \
 	  else								      \
 	    {								      \
 	      if (number.word != 0 && alt && base == 16)		      \
 		{							      \
 		  *string-- = spec;					      \
-		  *string-- = '0';					      \
+		  *string-- = L_('0');					      \
 		}							      \
 									      \
 	      if (is_negative)						      \
-		*string-- = '-';					      \
+		*string-- = L_('-');					      \
 	      else if (showsign)					      \
-		*string-- = '+';					      \
+		*string-- = L_('+');					      \
 	      else if (space)						      \
-		*string-- = ' ';					      \
+		*string-- = L_(' ');					      \
 									      \
 	      while (width-- > 0)					      \
-		*string-- = ' ';					      \
+		*string-- = L_(' ');					      \
 	    }								      \
 									      \
 	  outstring (string + 1, workend - string);			      \
@@ -732,20 +723,20 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	  if (number.word != 0 && alt && base == 16)			      \
 	    {								      \
 	      *string-- = spec;						      \
-	      *string-- = '0';						      \
+	      *string-- = L_('0');					      \
 	    }								      \
 									      \
 	  if (is_negative)						      \
-	    *string-- = '-';						      \
+	    *string-- = L_('-');					      \
 	  else if (showsign)						      \
-	    *string-- = '+';						      \
+	    *string-- = L_('+');					      \
 	  else if (space)						      \
-	    *string-- = ' ';						      \
+	    *string-- = L_(' ');					      \
 									      \
 	  width -= workend - string;					      \
 	  outstring (string + 1, workend - string);			      \
 									      \
-	  PAD (' ');							      \
+	  PAD (L_(' '));						      \
 	  break;							      \
 	}								      \
 									      \
@@ -771,7 +762,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 					showsign: showsign,		      \
 					group: group,			      \
 					pad: pad,			      \
-					extra: 0 };			      \
+					extra: 0,			      \
+					wide: sizeof (CHAR_T) != 1 };	      \
 									      \
 	    if (is_long_double)						      \
 	      the_arg.pa_long_double = va_arg (ap, long double);	      \
@@ -821,7 +813,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 					showsign: showsign,		      \
 					group: group,			      \
 					pad: pad,			      \
-					extra: 0 };			      \
+					extra: 0,			      \
+					wide: sizeof (CHAR_T) != 1 };	      \
 									      \
 	    if (is_long_double)						      \
 	      the_arg.pa_long_double = va_arg (ap, long double);	      \
@@ -849,6 +842,178 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       }									      \
       break;								      \
 									      \
+    LABEL (form_pointer):						      \
+      /* Generic pointer.  */						      \
+      {									      \
+	const void *ptr;						      \
+	if (fspec == NULL)						      \
+	  ptr = va_arg (ap, void *);					      \
+	else								      \
+	  ptr = args_value[fspec->data_arg].pa_pointer;			      \
+	if (ptr != NULL)						      \
+	  {								      \
+	    /* If the pointer is not NULL, write it as a %#x spec.  */	      \
+	    base = 16;							      \
+	    number.word = (unsigned long int) ptr;			      \
+	    is_negative = 0;						      \
+	    alt = 1;							      \
+	    group = 0;							      \
+	    spec = 'x';							      \
+	    goto LABEL (number);					      \
+	  }								      \
+	else								      \
+	  {								      \
+	    /* Write "(nil)" for a nil pointer.  */			      \
+	    string = (UCHAR_T *) L_("(nil)");				      \
+	    /* Make sure the full string "(nil)" is printed.  */	      \
+	    if (prec < 5)						      \
+	      prec = 5;							      \
+	    is_long = 0;	/* This is no wide-char string.  */	      \
+	    goto LABEL (print_string);					      \
+	  }								      \
+      }									      \
+      /* NOTREACHED */							      \
+									      \
+    LABEL (form_number):						      \
+      /* Answer the count of characters written.  */			      \
+      if (fspec == NULL)						      \
+	{								      \
+	  if (is_longlong)						      \
+	    *(long long int *) va_arg (ap, void *) = done;		      \
+	  else if (is_long_num)						      \
+	    *(long int *) va_arg (ap, void *) = done;			      \
+	  else if (!is_short)						      \
+	    *(int *) va_arg (ap, void *) = done;			      \
+	  else								      \
+	    *(short int *) va_arg (ap, void *) = done;			      \
+	}								      \
+      else								      \
+	if (is_longlong)						      \
+	  *(long long int *) args_value[fspec->data_arg].pa_pointer = done;   \
+	else if (is_long_num)						      \
+	  *(long int *) args_value[fspec->data_arg].pa_pointer = done;	      \
+	else if (!is_short)						      \
+	  *(int *) args_value[fspec->data_arg].pa_pointer = done;	      \
+	else								      \
+	  *(short int *) args_value[fspec->data_arg].pa_pointer = done;	      \
+      break;								      \
+									      \
+    LABEL (form_strerror):						      \
+      /* Print description of error ERRNO.  */				      \
+      string =								      \
+	(UCHAR_T *) __strerror_r (save_errno, (char *) work_buffer,	      \
+				  sizeof work_buffer);			      \
+      is_long = 0;		/* This is no wide-char string.  */	      \
+      goto LABEL (print_string)
+
+#ifdef COMPILE_WPRINTF
+# define process_string_arg(fspec) \
+    LABEL (form_character):						      \
+      /* Character.  */							      \
+      if (is_long)							      \
+	goto LABEL (form_wcharacter);					      \
+      --width;	/* Account for the character itself.  */		      \
+      if (!left)							      \
+	PAD (L' ');							      \
+      if (fspec == NULL)						      \
+	outchar (btowc ((unsigned char) va_arg (ap, int))); /* Promoted.  */  \
+      else								      \
+	outchar (btowc ((unsigned char) args_value[fspec->data_arg].pa_char));\
+      if (left)								      \
+	PAD (L' ');							      \
+      break;								      \
+									      \
+    LABEL (form_wcharacter):						      \
+      {									      \
+	/* Wide character.  */						      \
+	--width;							      \
+	if (!left)							      \
+	  PAD (L' ');							      \
+        if (fspec == NULL)						      \
+	  outchar (va_arg (ap, wint_t));				      \
+	else								      \
+	  outchar (args_value[fspec->data_arg].pa_wchar);		      \
+	if (left)							      \
+	  PAD (L' ');							      \
+      }									      \
+      break;								      \
+									      \
+    LABEL (form_string):						      \
+      {									      \
+	size_t len;							      \
+									      \
+	/* The string argument could in fact be `char *' or `wchar_t *'.      \
+	   But this should not make a difference here.  */		      \
+	if (fspec == NULL)						      \
+	  string = (UCHAR_T *) va_arg (ap, const wchar_t *);		      \
+	else								      \
+	  string = (UCHAR_T *) args_value[fspec->data_arg].pa_wstring;	      \
+									      \
+	/* Entry point for printing other strings.  */			      \
+      LABEL (print_string):						      \
+									      \
+	if (string == NULL)						      \
+	  {								      \
+	    /* Write "(null)" if there's space.  */			      \
+	    if (prec == -1						      \
+		|| prec >= (int) (sizeof (null) / sizeof (null[0])) - 1)      \
+	      {								      \
+		string = (UCHAR_T *) null;				      \
+		len = (sizeof (null) / sizeof (null[0])) - 1;		      \
+	      }								      \
+	    else							      \
+	      {								      \
+		string = (UCHAR_T *) L"";				      \
+		len = 0;						      \
+	      }								      \
+	  }								      \
+	else if (!is_long && spec != L_('S'))				      \
+	  {								      \
+	    /* This is complicated.  We have to transform the multibyte	      \
+	       string into a wide character string.  */			      \
+	    const char *mbs = (const char *) string;			      \
+	    mbstate_t mbstate;						      \
+									      \
+	    len = prec == -1 ? strnlen (mbs, prec) : strlen (mbs);	      \
+									      \
+	    /* Allocate dynamically an array which definitely is long	      \
+	       enough for the wide character version.  */		      \
+	    string = (UCHAR_T *) alloca ((len + 1) * sizeof (wchar_t));	      \
+									      \
+	    memset (&mbstate, '\0', sizeof (mbstate_t));		      \
+	    len = __mbsrtowcs ((wchar_t *) string, &mbs, len + 1, &mbstate);  \
+	    if (len == (size_t) -1)					      \
+	      {								      \
+		/* Illegal multibyte character.  */			      \
+		done = -1;						      \
+		goto all_done;						      \
+	      }								      \
+	  }								      \
+	else								      \
+	  {								      \
+	    if (prec != -1)						      \
+	      /* Search for the end of the string, but don't search past      \
+		 the length specified by the precision.  */		      \
+	      len = __wcsnlen ((wchar_t *) string, prec);		      \
+	    else							      \
+	      len = __wcslen ((wchar_t *) string);			      \
+	  }								      \
+									      \
+	if ((width -= len) < 0)						      \
+	  {								      \
+	    outstring (string, len);					      \
+	    break;							      \
+	  }								      \
+									      \
+	if (!left)							      \
+	  PAD (L' ');							      \
+	outstring (string, len);					      \
+	if (left)							      \
+	  PAD (L' ');							      \
+      }									      \
+      break;
+#else
+# define process_string_arg(fspec) \
     LABEL (form_character):						      \
       /* Character.  */							      \
       if (is_long)							      \
@@ -917,7 +1082,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    if (prec != -1)						      \
 	      /* Search for the end of the string, but don't search past      \
 		 the length specified by the precision.  */		      \
-	      len = strnlen (string, prec);				      \
+	      len = __strnlen (string, prec);				      \
 	    else							      \
 	      len = strlen (string);					      \
 	  }								      \
@@ -939,7 +1104,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    s2 = (const wchar_t *) string;				      \
 	    string = alloca (len + 1);					      \
 	    (void) __wcsrtombs (string, &s2, len + 1, &mbstate);	      \
-	    if (prec < len)						      \
+	    if (prec > 0 && prec < len)					      \
 	      len = prec;						      \
 	  }								      \
 									      \
@@ -955,75 +1120,23 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	if (left)							      \
 	  PAD (' ');							      \
       }									      \
-      break;								      \
-									      \
-    LABEL (form_pointer):						      \
-      /* Generic pointer.  */						      \
-      {									      \
-	const void *ptr;						      \
-	if (fspec == NULL)						      \
-	  ptr = va_arg (ap, void *);					      \
-	else								      \
-	  ptr = args_value[fspec->data_arg].pa_pointer;			      \
-	if (ptr != NULL)						      \
-	  {								      \
-	    /* If the pointer is not NULL, write it as a %#x spec.  */	      \
-	    base = 16;							      \
-	    number.word = (unsigned long int) ptr;			      \
-	    is_negative = 0;						      \
-	    alt = 1;							      \
-	    group = 0;							      \
-	    spec = 'x';							      \
-	    goto LABEL (number);					      \
-	  }								      \
-	else								      \
-	  {								      \
-	    /* Write "(nil)" for a nil pointer.  */			      \
-	    string = (char *) "(nil)";					      \
-	    /* Make sure the full string "(nil)" is printed.  */	      \
-	    if (prec < 5)						      \
-	      prec = 5;							      \
-	    is_long = 0;	/* This is no wide-char string.  */	      \
-	    goto LABEL (print_string);					      \
-	  }								      \
-      }									      \
-      /* NOTREACHED */							      \
-									      \
-    LABEL (form_number):						      \
-      /* Answer the count of characters written.  */			      \
-      if (fspec == NULL)						      \
-	{								      \
-	  if (is_longlong)						      \
-	    *(long long int *) va_arg (ap, void *) = done;		      \
-	  else if (is_long_num)						      \
-	    *(long int *) va_arg (ap, void *) = done;			      \
-	  else if (!is_short)						      \
-	    *(int *) va_arg (ap, void *) = done;			      \
-	  else								      \
-	    *(short int *) va_arg (ap, void *) = done;			      \
-	}								      \
-      else								      \
-	if (is_longlong)						      \
-	  *(long long int *) args_value[fspec->data_arg].pa_pointer = done;   \
-	else if (is_long_num)						      \
-	  *(long int *) args_value[fspec->data_arg].pa_pointer = done;	      \
-	else if (!is_short)						      \
-	  *(int *) args_value[fspec->data_arg].pa_pointer = done;	      \
-	else								      \
-	  *(short int *) args_value[fspec->data_arg].pa_pointer = done;	      \
-      break;								      \
-									      \
-    LABEL (form_strerror):						      \
-      /* Print description of error ERRNO.  */				      \
-      string =								      \
-	(char *) __strerror_r (save_errno, work_buffer, sizeof work_buffer);  \
-      is_long = 0;		/* This is no wide-char string.  */	      \
-      goto LABEL (print_string)
+      break;
+#endif
 
+  /* Orient the stream.  */
+#ifdef ORIENT
+  ORIENT;
+#endif
 
   /* Sanity check of arguments.  */
   ARGCHECK (s, format);
 
+  /* Check for correct orientation.  */
+  if (_IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
+      != (sizeof (CHAR_T) == 1 ? -1 : 1))
+    /* The stream is already oriented otherwise.  */
+    return EOF;
+
   if (UNBUFFERED_P (s))
     /* Use a helper function which will allocate a local temporary buffer
        for the stream and then call us again.  */
@@ -1041,11 +1154,16 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 #endif
   nspecs_done = 0;
 
+#ifdef COMPILE_WPRINTF
+  /* Find the first format specifier.  */
+  f = lead_str_end = find_spec ((const UCHAR_T *) format);
+#else
   /* Put state for processing format string in initial state.  */
   memset (&mbstate, '\0', sizeof (mbstate_t));
 
   /* Find the first format specifier.  */
   f = lead_str_end = find_spec (format, &mbstate);
+#endif
 
   /* Lock stream.  */
 #ifdef USE_IN_LIBIO
@@ -1081,7 +1199,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       } number;
       int base;
       union printf_arg the_arg;
-      char *string;	/* Pointer to argument string.  */
+      UCHAR_T *string;	/* Pointer to argument string.  */
       int alt = 0;	/* Alternate format.  */
       int space = 0;	/* Use space prefix if no sign is needed.  */
       int left = 0;	/* Left-justify output.  */
@@ -1093,10 +1211,10 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       int is_char = 0;	/* Argument is promoted (unsigned) char.  */
       int width = 0;	/* Width of output; 0 means none specified.  */
       int prec = -1;	/* Precision of output; -1 means none specified.  */
-      char pad = ' ';	/* Padding character.  */
+      UCHAR_T pad = L_(' ');/* Padding character.  */
       CHAR_T spec;
 
-      workend = &work_buffer[sizeof (work_buffer) - 1];
+      workend = &work_buffer[sizeof (work_buffer) / sizeof (CHAR_T) - 1];
 
       /* Get current character in format string.  */
       JUMP (*++f, step0_jumps);
@@ -1172,10 +1290,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	    left = 1;
 	  }
 
-	if (width + 32 >= sizeof (work_buffer))
+	if (width + 32 >= sizeof (work_buffer) / sizeof (work_buffer[0]))
 	  /* We have to use a special buffer.  The "32" is just a safe
 	     bet for all the output which is not counted in the width.  */
-	  workend = alloca (width + 32) + (width + 31);
+	  workend = ((UCHAR_T *) alloca ((width + 32) * sizeof (CHAR_T))
+		     + (width + 31));
       }
       JUMP (*f, step1_jumps);
 
@@ -1183,10 +1302,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
     LABEL (width):
       width = read_int (&f);
 
-      if (width + 32 >= sizeof (work_buffer))
+      if (width + 32 >= sizeof (work_buffer) / sizeof (work_buffer[0]))
 	/* We have to use a special buffer.  The "32" is just a safe
 	   bet for all the output which is not counted in the width.  */
-	workend = alloca (width + 32) + (width + 31);
+	workend = ((UCHAR_T *) alloca ((width + 32) * sizeof (CHAR_T))
+		   + (width + 31));
       if (*f == L_('$'))
 	/* Oh, oh.  The argument comes from a positional parameter.  */
 	goto do_positional;
@@ -1213,7 +1333,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
 	prec = read_int (&f);
       else
 	prec = 0;
-      if (prec > width && prec + 32 > sizeof (work_buffer))
+      if (prec > width
+	  && prec + 32 > sizeof (work_buffer) / sizeof (work_buffer[0]))
 	workend = alloca (spec + 32) + (spec + 31);
       JUMP (*f, step2_jumps);
 
@@ -1258,6 +1379,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       while (1)
 	{
 	  process_arg (((struct printf_spec *) NULL));
+	  process_string_arg (((struct printf_spec *) NULL));
 
 	LABEL (form_unknown):
 	  if (spec == L_('\0'))
@@ -1276,7 +1398,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
       ++nspecs_done;
 
       /* Look for next format specifier.  */
+#ifdef COMPILE_WPRINTF
+      f = find_spec ((end_of_spec = ++f));
+#else
       f = find_spec ((end_of_spec = ++f), &mbstate);
+#endif
 
       /* Write the following constant string.  */
       outstring (end_of_spec, f - end_of_spec);
@@ -1301,7 +1427,7 @@ do_positional:
        attributes.  */
     size_t nargs = 0;
     int *args_type;
-    union printf_arg *args_value;
+    union printf_arg *args_value = NULL;
 
     /* Positional parameters refer to arguments directly.  This could
        also determine the maximum number of arguments.  Track the
@@ -1329,7 +1455,7 @@ do_positional:
 	  grouping = NULL;
       }
 
-    for (f = lead_str_end; *f != '\0'; f = specs[nspecs++].next_fmt)
+    for (f = lead_str_end; *f != L_('\0'); f = specs[nspecs++].next_fmt)
       {
 	if (nspecs >= nspecs_max)
 	  {
@@ -1356,8 +1482,12 @@ do_positional:
 	  }
 
 	/* Parse the format specifier.  */
+#ifdef COMPILE_WPRINTF
+	nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg);
+#else
 	nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg,
 				 &mbstate);
+#endif
       }
 
     /* Determine the number of arguments the format string consumes.  */
@@ -1449,7 +1579,7 @@ do_positional:
 	} number;
 	int base;
 	union printf_arg the_arg;
-	char *string;	/* Pointer to argument string.  */
+	UCHAR_T *string;	/* Pointer to argument string.  */
 
 	/* Fill variables from values in struct.  */
 	int alt = specs[nspecs_done].info.alt;
@@ -1498,8 +1628,10 @@ do_positional:
 	  }
 
 	/* Maybe the buffer is too small.  */
-	if (MAX (prec, width) + 32 > sizeof (work_buffer))
-	  workend = alloca (MAX (prec, width) + 32) + (MAX (prec, width) + 31);
+	if (MAX (prec, width) + 32 > sizeof (work_buffer) / sizeof (UCHAR_T))
+	  workend = ((UCHAR_T *) alloca ((MAX (prec, width) + 32)
+					 * sizeof (UCHAR_T))
+		     + (MAX (prec, width) + 31));
 
 	/* Process format specifiers.  */
 	while (1)
@@ -1507,6 +1639,7 @@ do_positional:
 	    JUMP (spec, step4_jumps);
 
 	    process_arg ((&specs[nspecs_done]));
+	    process_string_arg ((&specs[nspecs_done]));
 
 	  LABEL (form_unknown):
 	    {
@@ -1564,21 +1697,6 @@ all_done:
 
   return done;
 }
-
-#ifdef USE_IN_LIBIO
-# undef vfprintf
-# ifdef strong_alias
-/* This is for glibc.  */
-strong_alias (_IO_vfprintf, vfprintf);
-# else
-#  if defined __ELF__ || defined __GNU_LIBRARY__
-#   include <gnu-stabs.h>
-#   ifdef weak_alias
-weak_alias (_IO_vfprintf, vfprintf);
-#   endif
-#  endif
-# endif
-#endif
 
 /* Handle an unknown format specifier.  This prints out a canonicalized
    representation of the format spec itself.  */
@@ -1588,24 +1706,25 @@ printf_unknown (FILE *s, const struct printf_info *info,
 
 {
   int done = 0;
-  char work_buffer[MAX (info->width, info->spec) + 32];
-  char *const workend = &work_buffer[sizeof (work_buffer) - 1];
-  register char *w;
+  CHAR_T work_buffer[MAX (info->width, info->spec) + 32];
+  CHAR_T *const workend = &work_buffer[sizeof (work_buffer) / sizeof (CHAR_T)
+				      - 1];
+  register CHAR_T *w;
 
-  outchar ('%');
+  outchar (L_('%'));
 
   if (info->alt)
-    outchar ('#');
+    outchar (L_('#'));
   if (info->group)
-    outchar ('\'');
+    outchar (L_('\''));
   if (info->showsign)
-    outchar ('+');
+    outchar (L_('+'));
   else if (info->space)
-    outchar (' ');
+    outchar (L_(' '));
   if (info->left)
-    outchar ('-');
+    outchar (L_('-'));
   if (info->pad == '0')
-    outchar ('0');
+    outchar (L_('0'));
 
   if (info->width != 0)
     {
@@ -1622,7 +1741,7 @@ printf_unknown (FILE *s, const struct printf_info *info,
 	outchar (*w++);
     }
 
-  if (info->spec != '\0')
+  if (info->spec != L_('\0'))
     outchar (info->spec);
 
  all_done:
@@ -1631,13 +1750,13 @@ printf_unknown (FILE *s, const struct printf_info *info,
 
 /* Group the digits according to the grouping rules of the current locale.
    The interpretation of GROUPING is as in `struct lconv' from <locale.h>.  */
-static char *
+static UCHAR_T *
 internal_function
-group_number (CHAR_T *w, CHAR_T *rear_ptr, const CHAR_T *grouping,
+group_number (UCHAR_T *w, UCHAR_T *rear_ptr, const char *grouping,
 	      wchar_t thousands_sep)
 {
   int len;
-  char *src, *s;
+  UCHAR_T *src, *s;
 
   /* We treat all negative values like CHAR_MAX.  */
 
@@ -1648,8 +1767,9 @@ group_number (CHAR_T *w, CHAR_T *rear_ptr, const CHAR_T *grouping,
   len = *grouping;
 
   /* Copy existing string so that nothing gets overwritten.  */
-  src = (char *) alloca (rear_ptr - w);
-  s = (char *) __mempcpy (src, w + 1, rear_ptr - w) - 1;
+  src = (UCHAR_T *) alloca ((rear_ptr - w) * sizeof (UCHAR_T));
+  s = (UCHAR_T *) __mempcpy (src, w + 1,
+			     (rear_ptr - w) * sizeof (UCHAR_T)) - 1;
   w = rear_ptr;
 
   /* Process all characters in the string.  */
@@ -1699,12 +1819,22 @@ static int
 _IO_helper_overflow (_IO_FILE *s, int c)
 {
   _IO_FILE *target = ((struct helper_file*) s)->_put_stream;
+#ifdef COMPILE_WPRINTF
+  int used = s->_wide_data->_IO_write_ptr - s->_wide_data->_IO_write_base;
+  if (used)
+    {
+      _IO_size_t written = _IO_sputn (target, s->_wide_data->_IO_write_base,
+				      used);
+      s->_wide_data->_IO_write_ptr -= written;
+    }
+#else
   int used = s->_IO_write_ptr - s->_IO_write_base;
   if (used)
     {
       _IO_size_t written = _IO_sputn (target, s->_IO_write_base, used);
       s->_IO_write_ptr -= written;
     }
+#endif
   return PUTC (c, s);
 }
 
@@ -1735,16 +1865,18 @@ internal_function
 buffered_vfprintf (register _IO_FILE *s, const CHAR_T *format,
 		   _IO_va_list args)
 {
-  char buf[_IO_BUFSIZ];
+  CHAR_T buf[_IO_BUFSIZ];
   struct helper_file helper;
   register _IO_FILE *hp = (_IO_FILE *) &helper;
   int result, to_flush;
 
   /* Initialize helper.  */
   helper._put_stream = s;
-  hp->_IO_write_base = buf;
-  hp->_IO_write_ptr = buf;
-  hp->_IO_write_end = buf + sizeof buf;
+#ifdef COMPILE_WPRINTF
+  _IO_wsetp (hp, buf, buf + sizeof buf / sizeof (CHAR_T));
+#else
+  _IO_setp (hp, buf, buf + sizeof buf);
+#endif
   hp->_IO_file_flags = _IO_MAGIC|_IO_NO_READS;
 #if _IO_JUMPS_OFFSET
   hp->_vtable_offset = 0;
@@ -1756,14 +1888,24 @@ buffered_vfprintf (register _IO_FILE *s, const CHAR_T *format,
   _IO_JUMPS (hp) = (struct _IO_jump_t *) &_IO_helper_jumps;
 
   /* Now print to helper instead.  */
-  result = _IO_vfprintf (hp, format, args);
+  result = vfprintf (hp, format, args);
 
   /* Now flush anything from the helper to the S. */
+#ifdef COMPILE_WPRINTF
+  if ((to_flush = (hp->_wide_data->_IO_write_ptr
+		   - hp->_wide_data->_IO_write_base)) > 0)
+    {
+      if ((int) _IO_sputn (s, hp->_wide_data->_IO_write_base, to_flush)
+	  != to_flush)
+	return -1;
+    }
+#else
   if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0)
     {
       if ((int) _IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush)
 	return -1;
     }
+#endif
 
   return result;
 }
@@ -1826,3 +1968,26 @@ __wprintf_pad (FILE *s, wchar_t pad, size_t count)
 }
 #undef PADSIZE
 #endif /* USE_IN_LIBIO */
+
+#ifdef USE_IN_LIBIO
+# undef vfprintf
+# ifdef strong_alias
+/* This is for glibc.  */
+#  ifdef COMPILE_WPRINTF
+strong_alias (_IO_vfwprintf, vfwprintf);
+#  else
+strong_alias (_IO_vfprintf, vfprintf);
+#  endif
+# else
+#  if defined __ELF__ || defined __GNU_LIBRARY__
+#   include <gnu-stabs.h>
+#   ifdef weak_alias
+#    ifdef COMPILE_WPRINTF
+weak_alias (_IO_vfwprintf, vfwprintf);
+#    else
+weak_alias (_IO_vfprintf, vfprintf);
+#    endif
+#   endif
+#  endif
+# endif
+#endif
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 0339edbeca..5caf616be3 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -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>
@@ -69,13 +70,56 @@
 
 # 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);			      \
@@ -94,7 +138,7 @@
 			  __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);			      \
@@ -180,30 +224,39 @@
    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
diff --git a/stdio-common/vfwprintf.c b/stdio-common/vfwprintf.c
new file mode 100644
index 0000000000..2c3cd06fad
--- /dev/null
+++ b/stdio-common/vfwprintf.c
@@ -0,0 +1,3 @@
+#include <wctype.h>
+#define COMPILE_WPRINTF	1
+#include "vfprintf.c"
diff --git a/stdio-common/vfwscanf.c b/stdio-common/vfwscanf.c
new file mode 100644
index 0000000000..62220bdccc
--- /dev/null
+++ b/stdio-common/vfwscanf.c
@@ -0,0 +1,2 @@
+#define COMPILE_WPRINTF	1
+#include "vfscanf.c"