]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - libio/wfileops.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / libio / wfileops.c
index 52368abb601a13b7d0b9aedd82480962837664df..82a9a59d5e07454b7742872828554295a0c8ec70 100644 (file)
@@ -1,29 +1,30 @@
-/* Copyright (C) 1993, 95, 97, 98, 99, 2000 Free Software Foundation, Inc.
-   This file is part of the GNU IO Library.
+/* Copyright (C) 1993-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
    Written by Ulrich Drepper <drepper@cygnus.com>.
    Based on the single byte version by Per Bothner <bothner@cygnus.com>.
 
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2, or (at
-   your option) any later version.
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
-   This library is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
+   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
-   General Public License for more details.
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
-   along with this library; see the file COPYING.  If not, write to
-   the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
-   MA 02111-1307, USA.
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.
 
-   As a special exception, if you link this library with files
-   compiled with a GNU compiler to produce an executable, this does
-   not cause the resulting executable to be covered by the GNU General
-   Public License.  This exception does not however invalidate any
-   other reasons why the executable file might be covered by the GNU
-   General Public License.  */
+   As a special exception, if you link the code in this file with
+   files compiled with a GNU compiler to produce an executable,
+   that does not cause the resulting executable to be covered by
+   the GNU Lesser General Public License.  This exception does not
+   however invalidate any other reasons why the executable file
+   might be covered by the GNU Lesser General Public License.
+   This exception applies to code released by its copyright holders
+   in files containing the exception.  */
 
 #include <assert.h>
 #include <libioP.h>
 #include <stdlib.h>
 #include <string.h>
 
-
-_IO_FILE *
-_IO_wfile_setbuf (fp, p, len)
-     _IO_FILE *fp;
-     wchar_t *p;
-     _IO_ssize_t len;
-{
-  if (_IO_wdefault_setbuf (fp, p, len) == NULL)
-    return NULL;
-
-  fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr =
-    fp->_wide_data->_IO_write_end = fp->_wide_data->_IO_buf_base;
-  _IO_wsetg (fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base,
-            fp->_wide_data->_IO_buf_base);
-
-  return fp;
-}
-
-
 /* Convert TO_DO wide character from DATA to FP.
    Then mark FP as having empty buffers. */
 int
-_IO_wdo_write (fp, data, to_do)
-     _IO_FILE *fp;
-     const wchar_t *data;
-     _IO_size_t to_do;
+_IO_wdo_write (FILE *fp, const wchar_t *data, size_t to_do)
 {
-  struct _IO_codecvt *cc = &fp->_wide_data->_codecvt;
+  struct _IO_codecvt *cc = fp->_codecvt;
 
   if (to_do > 0)
     {
@@ -68,26 +47,41 @@ _IO_wdo_write (fp, data, to_do)
        {
          if (_IO_new_do_write (fp, fp->_IO_write_base,
                                fp->_IO_write_ptr - fp->_IO_write_base) == EOF)
-           return EOF;
+           return WEOF;
        }
 
       do
        {
          enum __codecvt_result result;
          const wchar_t *new_data;
+         char mb_buf[MB_LEN_MAX];
+         char *write_base, *write_ptr, *buf_end;
+
+         if (fp->_IO_write_ptr - fp->_IO_write_base < sizeof (mb_buf))
+           {
+             /* Make sure we have room for at least one multibyte
+                character.  */
+             write_ptr = write_base = mb_buf;
+             buf_end = mb_buf + sizeof (mb_buf);
+           }
+         else
+           {
+             write_ptr = fp->_IO_write_ptr;
+             write_base = fp->_IO_write_base;
+             buf_end = fp->_IO_buf_end;
+           }
 
          /* Now convert from the internal format into the external buffer.  */
-         result = (*cc->__codecvt_do_out) (cc, &fp->_wide_data->_IO_state,
-                                           data, data + to_do, &new_data,
-                                           fp->_IO_write_ptr,
-                                           fp->_IO_buf_end,
-                                           &fp->_IO_write_ptr);
+         result = __libio_codecvt_out (cc, &fp->_wide_data->_IO_state,
+                                       data, data + to_do, &new_data,
+                                       write_ptr,
+                                       buf_end,
+                                       &write_ptr);
 
          /* Write out what we produced so far.  */
-         if (_IO_new_do_write (fp, fp->_IO_write_base,
-                               fp->_IO_write_ptr - fp->_IO_write_base) == EOF)
+         if (_IO_new_do_write (fp, write_base, write_ptr - write_base) == EOF)
            /* Something went wrong.  */
-           return EOF;
+           return WEOF;
 
          to_do -= new_data - data;
 
@@ -106,25 +100,27 @@ _IO_wdo_write (fp, data, to_do)
             fp->_wide_data->_IO_buf_base);
   fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr
     = fp->_wide_data->_IO_buf_base;
-  fp->_wide_data->_IO_write_end = ((fp->_flags & (_IO_LINE_BUF+_IO_UNBUFFERED))
+  fp->_wide_data->_IO_write_end = ((fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
                                   ? fp->_wide_data->_IO_buf_base
                                   : fp->_wide_data->_IO_buf_end);
 
   return to_do == 0 ? 0 : WEOF;
 }
+libc_hidden_def (_IO_wdo_write)
 
 
 wint_t
-_IO_wfile_underflow (fp)
-     _IO_FILE *fp;
+_IO_wfile_underflow (FILE *fp)
 {
   struct _IO_codecvt *cd;
   enum __codecvt_result status;
-  _IO_ssize_t count;
-  int tries;
-  const char *read_ptr_copy;
+  ssize_t count;
 
-  if (fp->_flags & _IO_NO_READS)
+  /* C99 requires EOF to be "sticky".  */
+  if (fp->_flags & _IO_EOF_SEEN)
+    return WEOF;
+
+  if (__glibc_unlikely (fp->_flags & _IO_NO_READS))
     {
       fp->_flags |= _IO_ERR_SEEN;
       __set_errno (EBADF);
@@ -133,39 +129,36 @@ _IO_wfile_underflow (fp)
   if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
     return *fp->_wide_data->_IO_read_ptr;
 
-  cd = &fp->_wide_data->_codecvt;
+  cd = fp->_codecvt;
 
   /* Maybe there is something left in the external buffer.  */
   if (fp->_IO_read_ptr < fp->_IO_read_end)
     {
-      /* Convert it.  */
-      size_t avail_bytes = fp->_IO_read_end - fp->_IO_read_ptr;
-
-      if (avail_bytes >= (*cd->__codecvt_do_max_length) (cd))
-       {
-         /* There is more in the external.  */
-         const char *read_stop = (const char *) fp->_IO_read_ptr;
+      /* There is more in the external.  Convert it.  */
+      const char *read_stop = (const char *) fp->_IO_read_ptr;
 
-         fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
-         status = (*cd->__codecvt_do_in) (cd, &fp->_wide_data->_IO_state,
-                                          fp->_IO_read_ptr, fp->_IO_read_end,
-                                          &read_stop,
-                                          fp->_wide_data->_IO_read_end,
-                                          fp->_wide_data->_IO_buf_end,
-                                          &fp->_wide_data->_IO_read_end);
+      fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
+      fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
+       fp->_wide_data->_IO_buf_base;
+      status = __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
+                                  fp->_IO_read_ptr, fp->_IO_read_end,
+                                  &read_stop,
+                                  fp->_wide_data->_IO_read_ptr,
+                                  fp->_wide_data->_IO_buf_end,
+                                  &fp->_wide_data->_IO_read_end);
 
-         fp->_IO_read_ptr = (char *) read_stop;
+      fp->_IO_read_base = fp->_IO_read_ptr;
+      fp->_IO_read_ptr = (char *) read_stop;
 
-         /* If we managed to generate some text return the next character.  */
-         if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
-           return *fp->_wide_data->_IO_read_ptr;
+      /* If we managed to generate some text return the next character.  */
+      if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
+       return *fp->_wide_data->_IO_read_ptr;
 
-         if (status == __codecvt_error)
-           {
-             __set_errno (EILSEQ);
-             fp->_flags |= _IO_ERR_SEEN;
-             return WEOF;
-           }
+      if (status == __codecvt_error)
+       {
+         __set_errno (EILSEQ);
+         fp->_flags |= _IO_ERR_SEEN;
+         return WEOF;
        }
 
       /* Move the remaining content of the read buffer to the beginning.  */
@@ -179,9 +172,6 @@ _IO_wfile_underflow (fp)
     fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_read_end =
       fp->_IO_buf_base;
 
-  fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end =
-    fp->_IO_buf_base;
-
   if (fp->_IO_buf_base == NULL)
     {
       /* Maybe we already have a push back pointer.  */
@@ -191,8 +181,14 @@ _IO_wfile_underflow (fp)
          fp->_flags &= ~_IO_IN_BACKUP;
        }
       _IO_doallocbuf (fp);
+
+      fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_read_end =
+       fp->_IO_buf_base;
     }
 
+  fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end =
+    fp->_IO_buf_base;
+
   if (fp->_wide_data->_IO_buf_base == NULL)
     {
       /* Maybe we already have a push back pointer.  */
@@ -204,41 +200,53 @@ _IO_wfile_underflow (fp)
       _IO_wdoallocbuf (fp);
     }
 
-  /* Flush all line buffered files before reading. */
   /* FIXME This can/should be moved to genops ?? */
-  if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
-    _IO_flush_all_linebuffered ();
+  if (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
+    {
+      /* We used to flush all line-buffered stream.  This really isn't
+        required by any standard.  My recollection is that
+        traditional Unix systems did this for stdout.  stderr better
+        not be line buffered.  So we do just that here
+        explicitly.  --drepper */
+      _IO_acquire_lock (stdout);
+
+      if ((stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))
+         == (_IO_LINKED | _IO_LINE_BUF))
+       _IO_OVERFLOW (stdout, EOF);
+
+      _IO_release_lock (stdout);
+    }
 
   _IO_switch_to_get_mode (fp);
 
-  fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;
-  fp->_IO_read_end = fp->_IO_buf_base;
-  fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end
-    = fp->_IO_buf_base;
-
   fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
     fp->_wide_data->_IO_buf_base;
   fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_buf_base;
   fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr =
     fp->_wide_data->_IO_write_end = fp->_wide_data->_IO_buf_base;
 
-  tries = 0;
+  const char *read_ptr_copy;
+  char accbuf[MB_LEN_MAX];
+  size_t naccbuf = 0;
  again:
   count = _IO_SYSREAD (fp, fp->_IO_read_end,
                       fp->_IO_buf_end - fp->_IO_read_end);
   if (count <= 0)
     {
-      if (count == 0 && tries == 0)
-       fp->_flags |= _IO_EOF_SEEN;
+      if (count == 0 && naccbuf == 0)
+       {
+         fp->_flags |= _IO_EOF_SEEN;
+         fp->_offset = _IO_pos_BAD;
+       }
       else
        fp->_flags |= _IO_ERR_SEEN, count = 0;
     }
   fp->_IO_read_end += count;
   if (count == 0)
     {
-      if (tries != 0)
+      if (naccbuf != 0)
        /* There are some bytes in the external buffer but they don't
-           convert to anything.  */
+          convert to anything.  */
        __set_errno (EILSEQ);
       return WEOF;
     }
@@ -248,18 +256,31 @@ _IO_wfile_underflow (fp)
   /* Now convert the read input.  */
   fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
   fp->_IO_read_base = fp->_IO_read_ptr;
-  status = (*cd->__codecvt_do_in) (cd, &fp->_wide_data->_IO_state,
-                                  fp->_IO_read_ptr, fp->_IO_read_end,
-                                  &read_ptr_copy,
-                                  fp->_wide_data->_IO_read_end,
-                                  fp->_wide_data->_IO_buf_end,
-                                  &fp->_wide_data->_IO_read_end);
-
-  fp->_IO_read_ptr = (char *) read_ptr_copy;
+  const char *from = fp->_IO_read_ptr;
+  const char *to = fp->_IO_read_end;
+  size_t to_copy = count;
+  if (__glibc_unlikely (naccbuf != 0))
+    {
+      to_copy = MIN (sizeof (accbuf) - naccbuf, count);
+      to = __mempcpy (&accbuf[naccbuf], from, to_copy);
+      naccbuf += to_copy;
+      from = accbuf;
+    }
+  status = __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
+                              from, to, &read_ptr_copy,
+                              fp->_wide_data->_IO_read_end,
+                              fp->_wide_data->_IO_buf_end,
+                              &fp->_wide_data->_IO_read_end);
+
+  if (__glibc_unlikely (naccbuf != 0))
+    fp->_IO_read_ptr += MAX (0, read_ptr_copy - &accbuf[naccbuf - to_copy]);
+  else
+    fp->_IO_read_ptr = (char *) read_ptr_copy;
   if (fp->_wide_data->_IO_read_end == fp->_wide_data->_IO_buf_base)
     {
-      if (status == __codecvt_error || fp->_IO_read_end == fp->_IO_buf_end)
+      if (status == __codecvt_error)
        {
+       out_eilseq:
          __set_errno (EILSEQ);
          fp->_flags |= _IO_ERR_SEEN;
          return WEOF;
@@ -267,18 +288,124 @@ _IO_wfile_underflow (fp)
 
       /* The read bytes make no complete character.  Try reading again.  */
       assert (status == __codecvt_partial);
-      ++tries;
+
+      if (naccbuf == 0)
+       {
+         if (fp->_IO_read_base < fp->_IO_read_ptr)
+           {
+             /* Partially used the buffer for some input data that
+                produces no output.  */
+             size_t avail = fp->_IO_read_end - fp->_IO_read_ptr;
+             memmove (fp->_IO_read_base, fp->_IO_read_ptr, avail);
+             fp->_IO_read_ptr = fp->_IO_read_base;
+             fp->_IO_read_end -= avail;
+             goto again;
+           }
+         naccbuf = fp->_IO_read_end - fp->_IO_read_ptr;
+         if (naccbuf >= sizeof (accbuf))
+           goto out_eilseq;
+
+         memcpy (accbuf, fp->_IO_read_ptr, naccbuf);
+       }
+      else
+       {
+         size_t used = read_ptr_copy - accbuf;
+         if (used > 0)
+           {
+             memmove (accbuf, read_ptr_copy, naccbuf - used);
+             naccbuf -= used;
+           }
+
+         if (naccbuf == sizeof (accbuf))
+           goto out_eilseq;
+       }
+
+      fp->_IO_read_ptr = fp->_IO_read_end = fp->_IO_read_base;
+
       goto again;
     }
 
   return *fp->_wide_data->_IO_read_ptr;
 }
+libc_hidden_def (_IO_wfile_underflow)
+
+
+static wint_t
+_IO_wfile_underflow_mmap (FILE *fp)
+{
+  struct _IO_codecvt *cd;
+  const char *read_stop;
+
+  if (__glibc_unlikely (fp->_flags & _IO_NO_READS))
+    {
+      fp->_flags |= _IO_ERR_SEEN;
+      __set_errno (EBADF);
+      return WEOF;
+    }
+  if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
+    return *fp->_wide_data->_IO_read_ptr;
+
+  cd = fp->_codecvt;
+
+  /* Maybe there is something left in the external buffer.  */
+  if (fp->_IO_read_ptr >= fp->_IO_read_end
+      /* No.  But maybe the read buffer is not fully set up.  */
+      && _IO_file_underflow_mmap (fp) == EOF)
+    /* Nothing available.  _IO_file_underflow_mmap has set the EOF or error
+       flags as appropriate.  */
+    return WEOF;
+
+  /* There is more in the external.  Convert it.  */
+  read_stop = (const char *) fp->_IO_read_ptr;
+
+  if (fp->_wide_data->_IO_buf_base == NULL)
+    {
+      /* Maybe we already have a push back pointer.  */
+      if (fp->_wide_data->_IO_save_base != NULL)
+       {
+         free (fp->_wide_data->_IO_save_base);
+         fp->_flags &= ~_IO_IN_BACKUP;
+       }
+      _IO_wdoallocbuf (fp);
+    }
+
+  fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
+  fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
+    fp->_wide_data->_IO_buf_base;
+  __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
+                     fp->_IO_read_ptr, fp->_IO_read_end,
+                     &read_stop,
+                     fp->_wide_data->_IO_read_ptr,
+                     fp->_wide_data->_IO_buf_end,
+                     &fp->_wide_data->_IO_read_end);
+
+  fp->_IO_read_ptr = (char *) read_stop;
+
+  /* If we managed to generate some text return the next character.  */
+  if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
+    return *fp->_wide_data->_IO_read_ptr;
+
+  /* There is some garbage at the end of the file.  */
+  __set_errno (EILSEQ);
+  fp->_flags |= _IO_ERR_SEEN;
+  return WEOF;
+}
+
+static wint_t
+_IO_wfile_underflow_maybe_mmap (FILE *fp)
+{
+  /* This is the first read attempt.  Doing the underflow will choose mmap
+     or vanilla operations and then punt to the chosen underflow routine.
+     Then we can punt to ours.  */
+  if (_IO_file_underflow_maybe_mmap (fp) == EOF)
+    return WEOF;
+
+  return _IO_WUNDERFLOW (fp);
+}
 
 
 wint_t
-_IO_wfile_overflow (f, wch)
-     _IO_FILE *f;
-     wint_t wch;
+_IO_wfile_overflow (FILE *f, wint_t wch)
 {
   if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
     {
@@ -293,6 +420,7 @@ _IO_wfile_overflow (f, wch)
       if (f->_wide_data->_IO_write_base == 0)
        {
          _IO_wdoallocbuf (f);
+         _IO_free_wbackup_area (f);
          _IO_wsetg (f, f->_wide_data->_IO_buf_base,
                     f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);
 
@@ -331,28 +459,28 @@ _IO_wfile_overflow (f, wch)
       f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
 
       f->_flags |= _IO_CURRENTLY_PUTTING;
-      if (f->_flags & (_IO_LINE_BUF+_IO_UNBUFFERED))
+      if (f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
        f->_wide_data->_IO_write_end = f->_wide_data->_IO_write_ptr;
     }
   if (wch == WEOF)
     return _IO_do_flush (f);
   if (f->_wide_data->_IO_write_ptr == f->_wide_data->_IO_buf_end)
     /* Buffer is really full */
-    if (_IO_do_flush (f) == WEOF)
+    if (_IO_do_flush (f) == EOF)
       return WEOF;
   *f->_wide_data->_IO_write_ptr++ = wch;
   if ((f->_flags & _IO_UNBUFFERED)
       || ((f->_flags & _IO_LINE_BUF) && wch == L'\n'))
-    if (_IO_do_flush (f) == WEOF)
+    if (_IO_do_flush (f) == EOF)
       return WEOF;
   return wch;
 }
+libc_hidden_def (_IO_wfile_overflow)
 
 wint_t
-_IO_wfile_sync (fp)
-     _IO_FILE *fp;
+_IO_wfile_sync (FILE *fp)
 {
-  _IO_ssize_t delta;
+  ssize_t delta;
   wint_t retval = 0;
 
   /*    char* ptr = cur_ptr(); */
@@ -364,10 +492,10 @@ _IO_wfile_sync (fp)
     {
       /* We have to find out how many bytes we have to go back in the
         external buffer.  */
-      struct _IO_codecvt *cv = &fp->_wide_data->_codecvt;
-      _IO_off64_t new_pos;
+      struct _IO_codecvt *cv = fp->_codecvt;
+      off64_t new_pos;
 
-      int clen = (*cv->__codecvt_do_encoding) (cv);
+      int clen = __libio_codecvt_encoding (cv);
 
       if (clen > 0)
        /* It is easy, a fixed number of input bytes are used for each
@@ -376,29 +504,28 @@ _IO_wfile_sync (fp)
       else
        {
          /* We have to find out the hard way how much to back off.
-             To do this we determine how much input we needed to
-             generate the wide characters up to the current reading
-             position.  */
+            To do this we determine how much input we needed to
+            generate the wide characters up to the current reading
+            position.  */
          int nread;
-
+         size_t wnread = (fp->_wide_data->_IO_read_ptr
+                          - fp->_wide_data->_IO_read_base);
          fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
-         nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
-                                             fp->_IO_read_base,
-                                             fp->_IO_read_end, delta);
+         nread = __libio_codecvt_length (cv, &fp->_wide_data->_IO_state,
+                                         fp->_IO_read_base,
+                                         fp->_IO_read_end, wnread);
          fp->_IO_read_ptr = fp->_IO_read_base + nread;
          delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
        }
 
       new_pos = _IO_SYSSEEK (fp, delta, 1);
-      if (new_pos != (_IO_off64_t) EOF)
+      if (new_pos != (off64_t) EOF)
        {
          fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
          fp->_IO_read_end = fp->_IO_read_ptr;
        }
-#ifdef ESPIPE
       else if (errno == ESPIPE)
        ; /* Ignore error from unseekable devices. */
-#endif
       else
        retval = WEOF;
     }
@@ -408,17 +535,223 @@ _IO_wfile_sync (fp)
   /*    setg(base(), ptr, ptr); */
   return retval;
 }
+libc_hidden_def (_IO_wfile_sync)
+
+/* Adjust the internal buffer pointers to reflect the state in the external
+   buffer.  The content between fp->_IO_read_base and fp->_IO_read_ptr is
+   assumed to be converted and available in the range
+   fp->_wide_data->_IO_read_base and fp->_wide_data->_IO_read_end.
+
+   Returns 0 on success and -1 on error with the _IO_ERR_SEEN flag set.  */
+static int
+adjust_wide_data (FILE *fp, bool do_convert)
+{
+  struct _IO_codecvt *cv = fp->_codecvt;
+
+  int clen = __libio_codecvt_encoding (cv);
+
+  /* Take the easy way out for constant length encodings if we don't need to
+     convert.  */
+  if (!do_convert && clen > 0)
+    {
+      fp->_wide_data->_IO_read_end += ((fp->_IO_read_ptr - fp->_IO_read_base)
+                                      / clen);
+      goto done;
+    }
+
+  enum __codecvt_result status;
+  const char *read_stop = (const char *) fp->_IO_read_base;
+  do
+    {
+
+      fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
+      status = __libio_codecvt_in (cv, &fp->_wide_data->_IO_state,
+                                  fp->_IO_read_base, fp->_IO_read_ptr,
+                                  &read_stop,
+                                  fp->_wide_data->_IO_read_base,
+                                  fp->_wide_data->_IO_buf_end,
+                                  &fp->_wide_data->_IO_read_end);
+
+      /* Should we return EILSEQ?  */
+      if (__glibc_unlikely (status == __codecvt_error))
+       {
+         fp->_flags |= _IO_ERR_SEEN;
+         return -1;
+       }
+    }
+  while (__builtin_expect (status == __codecvt_partial, 0));
+
+done:
+  /* Now seek to _IO_read_end to behave as if we have read it all in.  */
+  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
+
+  return 0;
+}
+
+/* ftell{,o} implementation for wide mode.  Don't modify any state of the file
+   pointer while we try to get the current state of the stream except in one
+   case, which is when we have unflushed writes in append mode.  */
+static off64_t
+do_ftell_wide (FILE *fp)
+{
+  off64_t result, offset = 0;
+
+  /* No point looking for offsets in the buffer if it hasn't even been
+     allocated.  */
+  if (fp->_wide_data->_IO_buf_base != NULL)
+    {
+      const wchar_t *wide_read_base;
+      const wchar_t *wide_read_ptr;
+      const wchar_t *wide_read_end;
+      bool unflushed_writes = (fp->_wide_data->_IO_write_ptr
+                              > fp->_wide_data->_IO_write_base);
+
+      bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING;
+
+      /* When we have unflushed writes in append mode, seek to the end of the
+        file and record that offset.  This is the only time we change the file
+        stream state and it is safe since the file handle is active.  */
+      if (unflushed_writes && append_mode)
+       {
+         result = _IO_SYSSEEK (fp, 0, _IO_seek_end);
+         if (result == _IO_pos_BAD)
+           return EOF;
+         else
+           fp->_offset = result;
+       }
+
+      /* XXX For wide stream with backup store it is not very
+        reasonable to determine the offset.  The pushed-back
+        character might require a state change and we need not be
+        able to compute the initial state by reverse transformation
+        since there is no guarantee of symmetry.  So we don't even
+        try and return an error.  */
+      if (_IO_in_backup (fp))
+       {
+         if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
+           {
+             __set_errno (EINVAL);
+             return -1;
+           }
+
+         /* Nothing in the backup store, so note the backed up pointers
+            without changing the state.  */
+         wide_read_base = fp->_wide_data->_IO_save_base;
+         wide_read_ptr = wide_read_base;
+         wide_read_end = fp->_wide_data->_IO_save_end;
+       }
+      else
+       {
+         wide_read_base = fp->_wide_data->_IO_read_base;
+         wide_read_ptr = fp->_wide_data->_IO_read_ptr;
+         wide_read_end = fp->_wide_data->_IO_read_end;
+       }
+
+      struct _IO_codecvt *cv = fp->_codecvt;
+      int clen = __libio_codecvt_encoding (cv);
+
+      if (!unflushed_writes)
+       {
+         if (clen > 0)
+           {
+             offset -= (wide_read_end - wide_read_ptr) * clen;
+             offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+           }
+         else
+           {
+             int nread;
+
+             size_t delta = wide_read_ptr - wide_read_base;
+             __mbstate_t state = fp->_wide_data->_IO_last_state;
+             nread = __libio_codecvt_length (cv, &state,
+                                             fp->_IO_read_base,
+                                             fp->_IO_read_end, delta);
+             offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
+           }
+       }
+      else
+       {
+         if (clen > 0)
+           offset += (fp->_wide_data->_IO_write_ptr
+                      - fp->_wide_data->_IO_write_base) * clen;
+         else
+           {
+             size_t delta = (fp->_wide_data->_IO_write_ptr
+                             - fp->_wide_data->_IO_write_base);
+
+             /* Allocate enough space for the conversion.  */
+             size_t outsize = delta * sizeof (wchar_t);
+             char *out = malloc (outsize);
+             char *outstop = out;
+             const wchar_t *in = fp->_wide_data->_IO_write_base;
+
+             enum __codecvt_result status;
+
+             __mbstate_t state = fp->_wide_data->_IO_last_state;
+             status = __libio_codecvt_out (cv, &state, in, in + delta, &in,
+                                           out, out + outsize, &outstop);
+
+             /* We don't check for __codecvt_partial because it can be
+                returned on one of two conditions: either the output
+                buffer is full or the input sequence is incomplete.  We
+                take care to allocate enough buffer and our input
+                sequences must be complete since they are accepted as
+                wchar_t; if not, then that is an error.  */
+             if (__glibc_unlikely (status != __codecvt_ok))
+               {
+                 free (out);
+                 return WEOF;
+               }
+
+             offset += outstop - out;
+             free (out);
+           }
+
+         /* We don't trust _IO_read_end to represent the current file offset
+            when writing in append mode because the value would have to be
+            shifted to the end of the file during a flush.  Use the write base
+            instead, along with the new offset we got above when we did a seek
+            to the end of the file.  */
+         if (append_mode)
+           offset += fp->_IO_write_ptr - fp->_IO_write_base;
+         /* For all other modes, _IO_read_end represents the file offset.  */
+         else
+           offset += fp->_IO_write_ptr - fp->_IO_read_end;
+       }
+    }
+
+  if (fp->_offset != _IO_pos_BAD)
+    result = fp->_offset;
+  else
+    result = _IO_SYSSEEK (fp, 0, _IO_seek_cur);
+
+  if (result == EOF)
+    return result;
+
+  result += offset;
+
+  if (result < 0)
+    {
+      __set_errno (EINVAL);
+      return EOF;
+    }
+
+  return result;
+}
 
-_IO_off64_t
-_IO_wfile_seekoff (fp, offset, dir, mode)
-     _IO_FILE *fp;
-     _IO_off64_t offset;
-     int dir;
-     int mode;
+off64_t
+_IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
 {
-  _IO_off64_t result;
-  _IO_off64_t delta, new_offset;
+  off64_t result;
+  off64_t delta, new_offset;
   long int count;
+
+  /* Short-circuit into a separate function.  We don't want to mix any
+     functionality and we don't want to touch anything inside the FILE
+     object. */
+  if (mode == 0)
+    return do_ftell_wide (fp);
+
   /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
      offset of the underlying file must be exact.  */
   int must_be_exact = ((fp->_wide_data->_IO_read_base
@@ -426,21 +759,19 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
                       && (fp->_wide_data->_IO_write_base
                           == fp->_wide_data->_IO_write_ptr));
 
-  if (mode == 0)
-    dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
+  bool was_writing = ((fp->_wide_data->_IO_write_ptr
+                      > fp->_wide_data->_IO_write_base)
+                     || _IO_in_put_mode (fp));
 
   /* Flush unwritten characters.
      (This may do an unneeded write if we seek within the buffer.
      But to be able to switch to reading, we would need to set
-     egptr to ptr.  That can't be done in the current design,
+     egptr to pptr.  That can't be done in the current design,
      which assumes file_ptr() is eGptr.  Anyway, since we probably
      end up flushing when we close(), it doesn't make much difference.)
-     FIXME: simulate mem-papped files. */
-
-  if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
-      || _IO_in_put_mode (fp))
-    if (_IO_switch_to_wget_mode (fp))
-      return WEOF;
+     FIXME: simulate mem-mapped files. */
+  if (was_writing && _IO_switch_to_wget_mode (fp))
+    return WEOF;
 
   if (fp->_wide_data->_IO_buf_base == NULL)
     {
@@ -466,29 +797,40 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
 
     case _IO_seek_cur:
       /* Adjust for read-ahead (bytes is buffer).  To do this we must
-         find out which position in the external buffer corresponds to
-         the current position in the internal buffer.  */
-      cv = &fp->_wide_data->_codecvt;
-      clen = (*cv->__codecvt_do_encoding) (cv);
+        find out which position in the external buffer corresponds to
+        the current position in the internal buffer.  */
+      cv = fp->_codecvt;
+      clen = __libio_codecvt_encoding (cv);
 
-      if (clen > 0)
-       offset -= (fp->_wide_data->_IO_read_end
-                  - fp->_wide_data->_IO_read_ptr) * clen;
-      else
+      if (mode != 0 || !was_writing)
        {
-         int nread;
+         if (clen > 0)
+           {
+             offset -= (fp->_wide_data->_IO_read_end
+                        - fp->_wide_data->_IO_read_ptr) * clen;
+             /* Adjust by readahead in external buffer.  */
+             offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+           }
+         else
+           {
+             int nread;
 
-         delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_end;
-         fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
-         nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
+             delta = (fp->_wide_data->_IO_read_ptr
+                      - fp->_wide_data->_IO_read_base);
+             fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
+             nread = __libio_codecvt_length (cv,
+                                             &fp->_wide_data->_IO_state,
                                              fp->_IO_read_base,
                                              fp->_IO_read_end, delta);
-         fp->_IO_read_ptr = fp->_IO_read_base + nread;
-         offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
+             fp->_IO_read_ptr = fp->_IO_read_base + nread;
+             fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
+             offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
+           }
        }
 
       if (fp->_offset == _IO_pos_BAD)
        goto dumb;
+
       /* Make offset absolute, assuming current pointer is file_ptr(). */
       offset += fp->_offset;
 
@@ -498,7 +840,7 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
       break;
     case _IO_seek_end:
       {
-       struct _G_stat64 st;
+       struct stat64 st;
        if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode))
          {
            offset += st.st_size;
@@ -508,73 +850,37 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
          goto dumb;
       }
     }
-  /* At this point, dir==_IO_seek_set. */
 
-  /* If we are only interested in the current position we've found it now.  */
-  if (mode == 0)
-    return offset;
+  _IO_free_wbackup_area (fp);
+
+  /* At this point, dir==_IO_seek_set. */
 
   /* If destination is within current buffer, optimize: */
   if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
       && !_IO_in_backup (fp))
     {
-      /* Offset relative to start of main get area. */
-      _IO_off64_t rel_offset = (offset - fp->_offset
-                               + (fp->_IO_read_end - fp->_IO_read_base));
-      if (rel_offset >= 0)
+      off64_t start_offset = (fp->_offset
+                              - (fp->_IO_read_end - fp->_IO_buf_base));
+      if (offset >= start_offset && offset < fp->_offset)
        {
-#if 0
-         if (_IO_in_backup (fp))
-           _IO_switch_to_main_get_area (fp);
-#endif
-         if (rel_offset <= fp->_IO_read_end - fp->_IO_read_base)
-           {
-             fp->_IO_read_ptr = fp->_IO_read_base + rel_offset;
-             _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
-
-             /* Now set the pointer for the internal buffer.  This
-                 might be an iterative process.  Though the read
-                 pointer is somewhere in the current external buffer
-                 this does not mean we can convert this whole buffer
-                 at once fitting in the internal buffer.  */
-             do
-               {
+         _IO_setg (fp, fp->_IO_buf_base,
+                   fp->_IO_buf_base + (offset - start_offset),
+                   fp->_IO_read_end);
+         _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
+         _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
+                    fp->_wide_data->_IO_buf_base,
+                    fp->_wide_data->_IO_buf_base);
+         _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
+                    fp->_wide_data->_IO_buf_base);
+
+         if (adjust_wide_data (fp, false))
+           goto dumb;
 
-               }
-             while (0);
-
-             _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
-             goto resync;
-           }
-#ifdef TODO
-           /* If we have streammarkers, seek forward by reading ahead. */
-           if (_IO_have_markers (fp))
-             {
-               int to_skip = rel_offset
-                 - (fp->_IO_read_ptr - fp->_IO_read_base);
-               if (ignore (to_skip) != to_skip)
-                 goto dumb;
-               _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
-               goto resync;
-             }
-#endif
-       }
-#ifdef TODO
-      if (rel_offset < 0 && rel_offset >= Bbase () - Bptr ())
-       {
-         if (!_IO_in_backup (fp))
-           _IO_switch_to_backup_area (fp);
-         gbump (fp->_IO_read_end + rel_offset - fp->_IO_read_ptr);
          _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
          goto resync;
        }
-#endif
     }
 
-#ifdef TODO
-  _IO_unsave_markers (fp);
-#endif
-
   if (fp->_flags & _IO_NO_READS)
     goto dumb;
 
@@ -607,6 +913,13 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
   _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + delta,
            fp->_IO_buf_base + count);
   _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
+  _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
+            fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
+  _IO_wsetp (fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
+
+  if (adjust_wide_data (fp, true))
+    goto dumb;
+
   fp->_offset = result + count;
   _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
   return offset;
@@ -620,6 +933,10 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
       fp->_offset = result;
       _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
       _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
+      _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
+                fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
+      _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
+                fp->_wide_data->_IO_buf_base);
     }
   return result;
 
@@ -627,24 +944,22 @@ resync:
   /* We need to do it since it is possible that the file offset in
      the kernel may be changed behind our back. It may happen when
      we fopen a file and then do a fork. One process may access the
-     the file and the kernel file offset will be changed. */
+     file and the kernel file offset will be changed. */
   if (fp->_offset >= 0)
     _IO_SYSSEEK (fp, fp->_offset, 0);
 
   return offset;
 }
+libc_hidden_def (_IO_wfile_seekoff)
 
 
-_IO_size_t
-_IO_wfile_xsputn (f, data, n)
-     _IO_FILE *f;
-     const void *data;
-     _IO_size_t n;
+size_t
+_IO_wfile_xsputn (FILE *f, const void *data, size_t n)
 {
-  register const wchar_t *s = (const wchar_t *) data;
-  _IO_size_t to_do = n;
+  const wchar_t *s = (const wchar_t *) data;
+  size_t to_do = n;
   int must_flush = 0;
-  _IO_size_t count;
+  size_t count;
 
   if (n <= 0)
     return 0;
@@ -659,7 +974,7 @@ _IO_wfile_xsputn (f, data, n)
       count = f->_wide_data->_IO_buf_end - f->_wide_data->_IO_write_ptr;
       if (count >= n)
        {
-         register const wchar_t *p;
+         const wchar_t *p;
          for (p = s + n; p > s; )
            {
              if (*--p == L'\n')
@@ -678,19 +993,14 @@ _IO_wfile_xsputn (f, data, n)
        count = to_do;
       if (count > 20)
        {
-#ifdef _LIBC
          f->_wide_data->_IO_write_ptr =
            __wmempcpy (f->_wide_data->_IO_write_ptr, s, count);
-#else
-         wmemcpy (f->_wide_data->_IO_write_ptr, s, count);
-         f->_wide_data->_IO_write_ptr += count;
-#endif
          s += count;
        }
       else
        {
-         register wchar_t *p = f->_wide_data->_IO_write_ptr;
-         register int i = (int) count;
+         wchar_t *p = f->_wide_data->_IO_write_ptr;
+         int i = (int) count;
          while (--i >= 0)
            *p++ = *s++;
          f->_wide_data->_IO_write_ptr = p;
@@ -707,9 +1017,10 @@ _IO_wfile_xsputn (f, data, n)
 
   return n - to_do;
 }
+libc_hidden_def (_IO_wfile_xsputn)
 
 
-struct _IO_jump_t _IO_wfile_jumps =
+const struct _IO_jump_t _IO_wfile_jumps libio_vtable =
 {
   JUMP_INIT_DUMMY,
   JUMP_INIT(finish, _IO_new_file_finish),
@@ -732,3 +1043,53 @@ struct _IO_jump_t _IO_wfile_jumps =
   JUMP_INIT(showmanyc, _IO_default_showmanyc),
   JUMP_INIT(imbue, _IO_default_imbue)
 };
+libc_hidden_data_def (_IO_wfile_jumps)
+
+
+const struct _IO_jump_t _IO_wfile_jumps_mmap libio_vtable =
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT(finish, _IO_new_file_finish),
+  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
+  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_mmap),
+  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
+  JUMP_INIT(xsputn, _IO_wfile_xsputn),
+  JUMP_INIT(xsgetn, _IO_file_xsgetn),
+  JUMP_INIT(seekoff, _IO_wfile_seekoff),
+  JUMP_INIT(seekpos, _IO_default_seekpos),
+  JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
+  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
+  JUMP_INIT(doallocate, _IO_wfile_doallocate),
+  JUMP_INIT(read, _IO_file_read),
+  JUMP_INIT(write, _IO_new_file_write),
+  JUMP_INIT(seek, _IO_file_seek),
+  JUMP_INIT(close, _IO_file_close_mmap),
+  JUMP_INIT(stat, _IO_file_stat),
+  JUMP_INIT(showmanyc, _IO_default_showmanyc),
+  JUMP_INIT(imbue, _IO_default_imbue)
+};
+
+const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap libio_vtable =
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT(finish, _IO_new_file_finish),
+  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
+  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_maybe_mmap),
+  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
+  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
+  JUMP_INIT(xsputn, _IO_wfile_xsputn),
+  JUMP_INIT(xsgetn, _IO_file_xsgetn),
+  JUMP_INIT(seekoff, _IO_wfile_seekoff),
+  JUMP_INIT(seekpos, _IO_default_seekpos),
+  JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
+  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
+  JUMP_INIT(doallocate, _IO_wfile_doallocate),
+  JUMP_INIT(read, _IO_file_read),
+  JUMP_INIT(write, _IO_new_file_write),
+  JUMP_INIT(seek, _IO_file_seek),
+  JUMP_INIT(close, _IO_file_close),
+  JUMP_INIT(stat, _IO_file_stat),
+  JUMP_INIT(showmanyc, _IO_default_showmanyc),
+  JUMP_INIT(imbue, _IO_default_imbue)
+};