(root)/
glibc-2.38/
libio/
wfileops.c
       1  /* Copyright (C) 1993-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.
      17  
      18     As a special exception, if you link the code in this file with
      19     files compiled with a GNU compiler to produce an executable,
      20     that does not cause the resulting executable to be covered by
      21     the GNU Lesser General Public License.  This exception does not
      22     however invalidate any other reasons why the executable file
      23     might be covered by the GNU Lesser General Public License.
      24     This exception applies to code released by its copyright holders
      25     in files containing the exception.  */
      26  
      27  #include <assert.h>
      28  #include <libioP.h>
      29  #include <wchar.h>
      30  #include <gconv.h>
      31  #include <stdlib.h>
      32  #include <string.h>
      33  
      34  /* Convert TO_DO wide character from DATA to FP.
      35     Then mark FP as having empty buffers. */
      36  int
      37  _IO_wdo_write (FILE *fp, const wchar_t *data, size_t to_do)
      38  {
      39    struct _IO_codecvt *cc = fp->_codecvt;
      40  
      41    if (to_do > 0)
      42      {
      43        if (fp->_IO_write_end == fp->_IO_write_ptr
      44  	  && fp->_IO_write_end != fp->_IO_write_base)
      45  	{
      46  	  if (_IO_new_do_write (fp, fp->_IO_write_base,
      47  				fp->_IO_write_ptr - fp->_IO_write_base) == EOF)
      48  	    return WEOF;
      49  	}
      50  
      51        do
      52  	{
      53  	  enum __codecvt_result result;
      54  	  const wchar_t *new_data;
      55  	  char mb_buf[MB_LEN_MAX];
      56  	  char *write_base, *write_ptr, *buf_end;
      57  
      58  	  if (fp->_IO_write_ptr - fp->_IO_write_base < sizeof (mb_buf))
      59  	    {
      60  	      /* Make sure we have room for at least one multibyte
      61  		 character.  */
      62  	      write_ptr = write_base = mb_buf;
      63  	      buf_end = mb_buf + sizeof (mb_buf);
      64  	    }
      65  	  else
      66  	    {
      67  	      write_ptr = fp->_IO_write_ptr;
      68  	      write_base = fp->_IO_write_base;
      69  	      buf_end = fp->_IO_buf_end;
      70  	    }
      71  
      72  	  /* Now convert from the internal format into the external buffer.  */
      73  	  result = __libio_codecvt_out (cc, &fp->_wide_data->_IO_state,
      74  					data, data + to_do, &new_data,
      75  					write_ptr,
      76  					buf_end,
      77  					&write_ptr);
      78  
      79  	  /* Write out what we produced so far.  */
      80  	  if (_IO_new_do_write (fp, write_base, write_ptr - write_base) == EOF)
      81  	    /* Something went wrong.  */
      82  	    return WEOF;
      83  
      84  	  to_do -= new_data - data;
      85  
      86  	  /* Next see whether we had problems during the conversion.  If yes,
      87  	     we cannot go on.  */
      88  	  if (result != __codecvt_ok
      89  	      && (result != __codecvt_partial || new_data - data == 0))
      90  	    break;
      91  
      92  	  data = new_data;
      93  	}
      94        while (to_do > 0);
      95      }
      96  
      97    _IO_wsetg (fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base,
      98  	     fp->_wide_data->_IO_buf_base);
      99    fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr
     100      = fp->_wide_data->_IO_buf_base;
     101    fp->_wide_data->_IO_write_end = ((fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
     102  				   ? fp->_wide_data->_IO_buf_base
     103  				   : fp->_wide_data->_IO_buf_end);
     104  
     105    return to_do == 0 ? 0 : WEOF;
     106  }
     107  libc_hidden_def (_IO_wdo_write)
     108  
     109  
     110  wint_t
     111  _IO_wfile_underflow (FILE *fp)
     112  {
     113    struct _IO_codecvt *cd;
     114    enum __codecvt_result status;
     115    ssize_t count;
     116  
     117    /* C99 requires EOF to be "sticky".  */
     118    if (fp->_flags & _IO_EOF_SEEN)
     119      return WEOF;
     120  
     121    if (__glibc_unlikely (fp->_flags & _IO_NO_READS))
     122      {
     123        fp->_flags |= _IO_ERR_SEEN;
     124        __set_errno (EBADF);
     125        return WEOF;
     126      }
     127    if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
     128      return *fp->_wide_data->_IO_read_ptr;
     129  
     130    cd = fp->_codecvt;
     131  
     132    /* Maybe there is something left in the external buffer.  */
     133    if (fp->_IO_read_ptr < fp->_IO_read_end)
     134      {
     135        /* There is more in the external.  Convert it.  */
     136        const char *read_stop = (const char *) fp->_IO_read_ptr;
     137  
     138        fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
     139        fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
     140  	fp->_wide_data->_IO_buf_base;
     141        status = __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
     142  				   fp->_IO_read_ptr, fp->_IO_read_end,
     143  				   &read_stop,
     144  				   fp->_wide_data->_IO_read_ptr,
     145  				   fp->_wide_data->_IO_buf_end,
     146  				   &fp->_wide_data->_IO_read_end);
     147  
     148        fp->_IO_read_base = fp->_IO_read_ptr;
     149        fp->_IO_read_ptr = (char *) read_stop;
     150  
     151        /* If we managed to generate some text return the next character.  */
     152        if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
     153  	return *fp->_wide_data->_IO_read_ptr;
     154  
     155        if (status == __codecvt_error)
     156  	{
     157  	  __set_errno (EILSEQ);
     158  	  fp->_flags |= _IO_ERR_SEEN;
     159  	  return WEOF;
     160  	}
     161  
     162        /* Move the remaining content of the read buffer to the beginning.  */
     163        memmove (fp->_IO_buf_base, fp->_IO_read_ptr,
     164  	       fp->_IO_read_end - fp->_IO_read_ptr);
     165        fp->_IO_read_end = (fp->_IO_buf_base
     166  			  + (fp->_IO_read_end - fp->_IO_read_ptr));
     167        fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;
     168      }
     169    else
     170      fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_read_end =
     171        fp->_IO_buf_base;
     172  
     173    if (fp->_IO_buf_base == NULL)
     174      {
     175        /* Maybe we already have a push back pointer.  */
     176        if (fp->_IO_save_base != NULL)
     177  	{
     178  	  free (fp->_IO_save_base);
     179  	  fp->_flags &= ~_IO_IN_BACKUP;
     180  	}
     181        _IO_doallocbuf (fp);
     182  
     183        fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_read_end =
     184  	fp->_IO_buf_base;
     185      }
     186  
     187    fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end =
     188      fp->_IO_buf_base;
     189  
     190    if (fp->_wide_data->_IO_buf_base == NULL)
     191      {
     192        /* Maybe we already have a push back pointer.  */
     193        if (fp->_wide_data->_IO_save_base != NULL)
     194  	{
     195  	  free (fp->_wide_data->_IO_save_base);
     196  	  fp->_flags &= ~_IO_IN_BACKUP;
     197  	}
     198        _IO_wdoallocbuf (fp);
     199      }
     200  
     201    /* FIXME This can/should be moved to genops ?? */
     202    if (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
     203      {
     204        /* We used to flush all line-buffered stream.  This really isn't
     205  	 required by any standard.  My recollection is that
     206  	 traditional Unix systems did this for stdout.  stderr better
     207  	 not be line buffered.  So we do just that here
     208  	 explicitly.  --drepper */
     209        _IO_acquire_lock (stdout);
     210  
     211        if ((stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))
     212  	  == (_IO_LINKED | _IO_LINE_BUF))
     213  	_IO_OVERFLOW (stdout, EOF);
     214  
     215        _IO_release_lock (stdout);
     216      }
     217  
     218    _IO_switch_to_get_mode (fp);
     219  
     220    fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
     221      fp->_wide_data->_IO_buf_base;
     222    fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_buf_base;
     223    fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr =
     224      fp->_wide_data->_IO_write_end = fp->_wide_data->_IO_buf_base;
     225  
     226    const char *read_ptr_copy;
     227    char accbuf[MB_LEN_MAX];
     228    size_t naccbuf = 0;
     229   again:
     230    count = _IO_SYSREAD (fp, fp->_IO_read_end,
     231  		       fp->_IO_buf_end - fp->_IO_read_end);
     232    if (count <= 0)
     233      {
     234        if (count == 0 && naccbuf == 0)
     235  	{
     236  	  fp->_flags |= _IO_EOF_SEEN;
     237  	  fp->_offset = _IO_pos_BAD;
     238  	}
     239        else
     240  	fp->_flags |= _IO_ERR_SEEN, count = 0;
     241      }
     242    fp->_IO_read_end += count;
     243    if (count == 0)
     244      {
     245        if (naccbuf != 0)
     246  	/* There are some bytes in the external buffer but they don't
     247  	   convert to anything.  */
     248  	__set_errno (EILSEQ);
     249        return WEOF;
     250      }
     251    if (fp->_offset != _IO_pos_BAD)
     252      _IO_pos_adjust (fp->_offset, count);
     253  
     254    /* Now convert the read input.  */
     255    fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
     256    fp->_IO_read_base = fp->_IO_read_ptr;
     257    const char *from = fp->_IO_read_ptr;
     258    const char *to = fp->_IO_read_end;
     259    size_t to_copy = count;
     260    if (__glibc_unlikely (naccbuf != 0))
     261      {
     262        to_copy = MIN (sizeof (accbuf) - naccbuf, count);
     263        to = __mempcpy (&accbuf[naccbuf], from, to_copy);
     264        naccbuf += to_copy;
     265        from = accbuf;
     266      }
     267    status = __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
     268  			       from, to, &read_ptr_copy,
     269  			       fp->_wide_data->_IO_read_end,
     270  			       fp->_wide_data->_IO_buf_end,
     271  			       &fp->_wide_data->_IO_read_end);
     272  
     273    if (__glibc_unlikely (naccbuf != 0))
     274      fp->_IO_read_ptr += MAX (0, read_ptr_copy - &accbuf[naccbuf - to_copy]);
     275    else
     276      fp->_IO_read_ptr = (char *) read_ptr_copy;
     277    if (fp->_wide_data->_IO_read_end == fp->_wide_data->_IO_buf_base)
     278      {
     279        if (status == __codecvt_error)
     280  	{
     281  	out_eilseq:
     282  	  __set_errno (EILSEQ);
     283  	  fp->_flags |= _IO_ERR_SEEN;
     284  	  return WEOF;
     285  	}
     286  
     287        /* The read bytes make no complete character.  Try reading again.  */
     288        assert (status == __codecvt_partial);
     289  
     290        if (naccbuf == 0)
     291  	{
     292  	  if (fp->_IO_read_base < fp->_IO_read_ptr)
     293  	    {
     294  	      /* Partially used the buffer for some input data that
     295  		 produces no output.  */
     296  	      size_t avail = fp->_IO_read_end - fp->_IO_read_ptr;
     297  	      memmove (fp->_IO_read_base, fp->_IO_read_ptr, avail);
     298  	      fp->_IO_read_ptr = fp->_IO_read_base;
     299  	      fp->_IO_read_end -= avail;
     300  	      goto again;
     301  	    }
     302  	  naccbuf = fp->_IO_read_end - fp->_IO_read_ptr;
     303  	  if (naccbuf >= sizeof (accbuf))
     304  	    goto out_eilseq;
     305  
     306  	  memcpy (accbuf, fp->_IO_read_ptr, naccbuf);
     307  	}
     308        else
     309  	{
     310  	  size_t used = read_ptr_copy - accbuf;
     311  	  if (used > 0)
     312  	    {
     313  	      memmove (accbuf, read_ptr_copy, naccbuf - used);
     314  	      naccbuf -= used;
     315  	    }
     316  
     317  	  if (naccbuf == sizeof (accbuf))
     318  	    goto out_eilseq;
     319  	}
     320  
     321        fp->_IO_read_ptr = fp->_IO_read_end = fp->_IO_read_base;
     322  
     323        goto again;
     324      }
     325  
     326    return *fp->_wide_data->_IO_read_ptr;
     327  }
     328  libc_hidden_def (_IO_wfile_underflow)
     329  
     330  
     331  wint_t
     332  _IO_wfile_underflow_mmap (FILE *fp)
     333  {
     334    struct _IO_codecvt *cd;
     335    const char *read_stop;
     336  
     337    if (__glibc_unlikely (fp->_flags & _IO_NO_READS))
     338      {
     339        fp->_flags |= _IO_ERR_SEEN;
     340        __set_errno (EBADF);
     341        return WEOF;
     342      }
     343    if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
     344      return *fp->_wide_data->_IO_read_ptr;
     345  
     346    cd = fp->_codecvt;
     347  
     348    /* Maybe there is something left in the external buffer.  */
     349    if (fp->_IO_read_ptr >= fp->_IO_read_end
     350        /* No.  But maybe the read buffer is not fully set up.  */
     351        && _IO_file_underflow_mmap (fp) == EOF)
     352      /* Nothing available.  _IO_file_underflow_mmap has set the EOF or error
     353         flags as appropriate.  */
     354      return WEOF;
     355  
     356    /* There is more in the external.  Convert it.  */
     357    read_stop = (const char *) fp->_IO_read_ptr;
     358  
     359    if (fp->_wide_data->_IO_buf_base == NULL)
     360      {
     361        /* Maybe we already have a push back pointer.  */
     362        if (fp->_wide_data->_IO_save_base != NULL)
     363  	{
     364  	  free (fp->_wide_data->_IO_save_base);
     365  	  fp->_flags &= ~_IO_IN_BACKUP;
     366  	}
     367        _IO_wdoallocbuf (fp);
     368      }
     369  
     370    fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
     371    fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
     372      fp->_wide_data->_IO_buf_base;
     373    __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,
     374  		      fp->_IO_read_ptr, fp->_IO_read_end,
     375  		      &read_stop,
     376  		      fp->_wide_data->_IO_read_ptr,
     377  		      fp->_wide_data->_IO_buf_end,
     378  		      &fp->_wide_data->_IO_read_end);
     379  
     380    fp->_IO_read_ptr = (char *) read_stop;
     381  
     382    /* If we managed to generate some text return the next character.  */
     383    if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
     384      return *fp->_wide_data->_IO_read_ptr;
     385  
     386    /* There is some garbage at the end of the file.  */
     387    __set_errno (EILSEQ);
     388    fp->_flags |= _IO_ERR_SEEN;
     389    return WEOF;
     390  }
     391  
     392  wint_t
     393  _IO_wfile_underflow_maybe_mmap (FILE *fp)
     394  {
     395    /* This is the first read attempt.  Doing the underflow will choose mmap
     396       or vanilla operations and then punt to the chosen underflow routine.
     397       Then we can punt to ours.  */
     398    if (_IO_file_underflow_maybe_mmap (fp) == EOF)
     399      return WEOF;
     400  
     401    return _IO_WUNDERFLOW (fp);
     402  }
     403  
     404  
     405  wint_t
     406  _IO_wfile_overflow (FILE *f, wint_t wch)
     407  {
     408    if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
     409      {
     410        f->_flags |= _IO_ERR_SEEN;
     411        __set_errno (EBADF);
     412        return WEOF;
     413      }
     414    /* If currently reading or no buffer allocated. */
     415    if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0
     416        || f->_wide_data->_IO_write_base == NULL)
     417      {
     418        /* Allocate a buffer if needed. */
     419        if (f->_wide_data->_IO_write_base == 0)
     420  	{
     421  	  _IO_wdoallocbuf (f);
     422  	  _IO_free_wbackup_area (f);
     423  	  _IO_wsetg (f, f->_wide_data->_IO_buf_base,
     424  		     f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);
     425  
     426  	  if (f->_IO_write_base == NULL)
     427  	    {
     428  	      _IO_doallocbuf (f);
     429  	      _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
     430  	    }
     431  	}
     432        else
     433  	{
     434  	  /* Otherwise must be currently reading.  If _IO_read_ptr
     435  	     (and hence also _IO_read_end) is at the buffer end,
     436  	     logically slide the buffer forwards one block (by setting
     437  	     the read pointers to all point at the beginning of the
     438  	     block).  This makes room for subsequent output.
     439  	     Otherwise, set the read pointers to _IO_read_end (leaving
     440  	     that alone, so it can continue to correspond to the
     441  	     external position). */
     442  	  if (f->_wide_data->_IO_read_ptr == f->_wide_data->_IO_buf_end)
     443  	    {
     444  	      f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
     445  	      f->_wide_data->_IO_read_end = f->_wide_data->_IO_read_ptr =
     446  		f->_wide_data->_IO_buf_base;
     447  	    }
     448  	}
     449        f->_wide_data->_IO_write_ptr = f->_wide_data->_IO_read_ptr;
     450        f->_wide_data->_IO_write_base = f->_wide_data->_IO_write_ptr;
     451        f->_wide_data->_IO_write_end = f->_wide_data->_IO_buf_end;
     452        f->_wide_data->_IO_read_base = f->_wide_data->_IO_read_ptr =
     453  	f->_wide_data->_IO_read_end;
     454  
     455        f->_IO_write_ptr = f->_IO_read_ptr;
     456        f->_IO_write_base = f->_IO_write_ptr;
     457        f->_IO_write_end = f->_IO_buf_end;
     458        f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
     459  
     460        f->_flags |= _IO_CURRENTLY_PUTTING;
     461        if (f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
     462  	f->_wide_data->_IO_write_end = f->_wide_data->_IO_write_ptr;
     463      }
     464    if (wch == WEOF)
     465      return _IO_do_flush (f);
     466    if (f->_wide_data->_IO_write_ptr == f->_wide_data->_IO_buf_end)
     467      /* Buffer is really full */
     468      if (_IO_do_flush (f) == EOF)
     469        return WEOF;
     470    *f->_wide_data->_IO_write_ptr++ = wch;
     471    if ((f->_flags & _IO_UNBUFFERED)
     472        || ((f->_flags & _IO_LINE_BUF) && wch == L'\n'))
     473      if (_IO_do_flush (f) == EOF)
     474        return WEOF;
     475    return wch;
     476  }
     477  libc_hidden_def (_IO_wfile_overflow)
     478  
     479  wint_t
     480  _IO_wfile_sync (FILE *fp)
     481  {
     482    ssize_t delta;
     483    wint_t retval = 0;
     484  
     485    /*    char* ptr = cur_ptr(); */
     486    if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
     487      if (_IO_do_flush (fp))
     488        return WEOF;
     489    delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_end;
     490    if (delta != 0)
     491      {
     492        /* We have to find out how many bytes we have to go back in the
     493  	 external buffer.  */
     494        struct _IO_codecvt *cv = fp->_codecvt;
     495        off64_t new_pos;
     496  
     497        int clen = __libio_codecvt_encoding (cv);
     498  
     499        if (clen > 0)
     500  	/* It is easy, a fixed number of input bytes are used for each
     501  	   wide character.  */
     502  	delta *= clen;
     503        else
     504  	{
     505  	  /* We have to find out the hard way how much to back off.
     506  	     To do this we determine how much input we needed to
     507  	     generate the wide characters up to the current reading
     508  	     position.  */
     509  	  int nread;
     510  	  size_t wnread = (fp->_wide_data->_IO_read_ptr
     511  			   - fp->_wide_data->_IO_read_base);
     512  	  fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
     513  	  nread = __libio_codecvt_length (cv, &fp->_wide_data->_IO_state,
     514  					  fp->_IO_read_base,
     515  					  fp->_IO_read_end, wnread);
     516  	  fp->_IO_read_ptr = fp->_IO_read_base + nread;
     517  	  delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
     518  	}
     519  
     520        new_pos = _IO_SYSSEEK (fp, delta, 1);
     521        if (new_pos != (off64_t) EOF)
     522  	{
     523  	  fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
     524  	  fp->_IO_read_end = fp->_IO_read_ptr;
     525  	}
     526        else if (errno == ESPIPE)
     527  	; /* Ignore error from unseekable devices. */
     528        else
     529  	retval = WEOF;
     530      }
     531    if (retval != WEOF)
     532      fp->_offset = _IO_pos_BAD;
     533    /* FIXME: Cleanup - can this be shared? */
     534    /*    setg(base(), ptr, ptr); */
     535    return retval;
     536  }
     537  libc_hidden_def (_IO_wfile_sync)
     538  
     539  /* Adjust the internal buffer pointers to reflect the state in the external
     540     buffer.  The content between fp->_IO_read_base and fp->_IO_read_ptr is
     541     assumed to be converted and available in the range
     542     fp->_wide_data->_IO_read_base and fp->_wide_data->_IO_read_end.
     543  
     544     Returns 0 on success and -1 on error with the _IO_ERR_SEEN flag set.  */
     545  static int
     546  adjust_wide_data (FILE *fp, bool do_convert)
     547  {
     548    struct _IO_codecvt *cv = fp->_codecvt;
     549  
     550    int clen = __libio_codecvt_encoding (cv);
     551  
     552    /* Take the easy way out for constant length encodings if we don't need to
     553       convert.  */
     554    if (!do_convert && clen > 0)
     555      {
     556        fp->_wide_data->_IO_read_end += ((fp->_IO_read_ptr - fp->_IO_read_base)
     557  				       / clen);
     558        goto done;
     559      }
     560  
     561    enum __codecvt_result status;
     562    const char *read_stop = (const char *) fp->_IO_read_base;
     563    do
     564      {
     565  
     566        fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
     567        status = __libio_codecvt_in (cv, &fp->_wide_data->_IO_state,
     568  				   fp->_IO_read_base, fp->_IO_read_ptr,
     569  				   &read_stop,
     570  				   fp->_wide_data->_IO_read_base,
     571  				   fp->_wide_data->_IO_buf_end,
     572  				   &fp->_wide_data->_IO_read_end);
     573  
     574        /* Should we return EILSEQ?  */
     575        if (__glibc_unlikely (status == __codecvt_error))
     576  	{
     577  	  fp->_flags |= _IO_ERR_SEEN;
     578  	  return -1;
     579  	}
     580      }
     581    while (__builtin_expect (status == __codecvt_partial, 0));
     582  
     583  done:
     584    /* Now seek to _IO_read_end to behave as if we have read it all in.  */
     585    fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
     586  
     587    return 0;
     588  }
     589  
     590  /* ftell{,o} implementation for wide mode.  Don't modify any state of the file
     591     pointer while we try to get the current state of the stream except in one
     592     case, which is when we have unflushed writes in append mode.  */
     593  static off64_t
     594  do_ftell_wide (FILE *fp)
     595  {
     596    off64_t result, offset = 0;
     597  
     598    /* No point looking for offsets in the buffer if it hasn't even been
     599       allocated.  */
     600    if (fp->_wide_data->_IO_buf_base != NULL)
     601      {
     602        const wchar_t *wide_read_base;
     603        const wchar_t *wide_read_ptr;
     604        const wchar_t *wide_read_end;
     605        bool unflushed_writes = (fp->_wide_data->_IO_write_ptr
     606  			       > fp->_wide_data->_IO_write_base);
     607  
     608        bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING;
     609  
     610        /* When we have unflushed writes in append mode, seek to the end of the
     611  	 file and record that offset.  This is the only time we change the file
     612  	 stream state and it is safe since the file handle is active.  */
     613        if (unflushed_writes && append_mode)
     614  	{
     615  	  result = _IO_SYSSEEK (fp, 0, _IO_seek_end);
     616  	  if (result == _IO_pos_BAD)
     617  	    return EOF;
     618  	  else
     619  	    fp->_offset = result;
     620  	}
     621  
     622        /* XXX For wide stream with backup store it is not very
     623  	 reasonable to determine the offset.  The pushed-back
     624  	 character might require a state change and we need not be
     625  	 able to compute the initial state by reverse transformation
     626  	 since there is no guarantee of symmetry.  So we don't even
     627  	 try and return an error.  */
     628        if (_IO_in_backup (fp))
     629  	{
     630  	  if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
     631  	    {
     632  	      __set_errno (EINVAL);
     633  	      return -1;
     634  	    }
     635  
     636  	  /* Nothing in the backup store, so note the backed up pointers
     637  	     without changing the state.  */
     638  	  wide_read_base = fp->_wide_data->_IO_save_base;
     639  	  wide_read_ptr = wide_read_base;
     640  	  wide_read_end = fp->_wide_data->_IO_save_end;
     641  	}
     642        else
     643  	{
     644  	  wide_read_base = fp->_wide_data->_IO_read_base;
     645  	  wide_read_ptr = fp->_wide_data->_IO_read_ptr;
     646  	  wide_read_end = fp->_wide_data->_IO_read_end;
     647  	}
     648  
     649        struct _IO_codecvt *cv = fp->_codecvt;
     650        int clen = __libio_codecvt_encoding (cv);
     651  
     652        if (!unflushed_writes)
     653  	{
     654  	  if (clen > 0)
     655  	    {
     656  	      offset -= (wide_read_end - wide_read_ptr) * clen;
     657  	      offset -= fp->_IO_read_end - fp->_IO_read_ptr;
     658  	    }
     659  	  else
     660  	    {
     661  	      int nread;
     662  
     663  	      size_t delta = wide_read_ptr - wide_read_base;
     664  	      __mbstate_t state = fp->_wide_data->_IO_last_state;
     665  	      nread = __libio_codecvt_length (cv, &state,
     666  					      fp->_IO_read_base,
     667  					      fp->_IO_read_end, delta);
     668  	      offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
     669  	    }
     670  	}
     671        else
     672  	{
     673  	  if (clen > 0)
     674  	    offset += (fp->_wide_data->_IO_write_ptr
     675  		       - fp->_wide_data->_IO_write_base) * clen;
     676  	  else
     677  	    {
     678  	      size_t delta = (fp->_wide_data->_IO_write_ptr
     679  			      - fp->_wide_data->_IO_write_base);
     680  
     681  	      /* Allocate enough space for the conversion.  */
     682  	      size_t outsize = delta * sizeof (wchar_t);
     683  	      char *out = malloc (outsize);
     684  	      char *outstop = out;
     685  	      const wchar_t *in = fp->_wide_data->_IO_write_base;
     686  
     687  	      enum __codecvt_result status;
     688  
     689  	      __mbstate_t state = fp->_wide_data->_IO_last_state;
     690  	      status = __libio_codecvt_out (cv, &state, in, in + delta, &in,
     691  					    out, out + outsize, &outstop);
     692  
     693  	      /* We don't check for __codecvt_partial because it can be
     694  		 returned on one of two conditions: either the output
     695  		 buffer is full or the input sequence is incomplete.  We
     696  		 take care to allocate enough buffer and our input
     697  		 sequences must be complete since they are accepted as
     698  		 wchar_t; if not, then that is an error.  */
     699  	      if (__glibc_unlikely (status != __codecvt_ok))
     700  		{
     701  		  free (out);
     702  		  return WEOF;
     703  		}
     704  
     705  	      offset += outstop - out;
     706  	      free (out);
     707  	    }
     708  
     709  	  /* We don't trust _IO_read_end to represent the current file offset
     710  	     when writing in append mode because the value would have to be
     711  	     shifted to the end of the file during a flush.  Use the write base
     712  	     instead, along with the new offset we got above when we did a seek
     713  	     to the end of the file.  */
     714  	  if (append_mode)
     715  	    offset += fp->_IO_write_ptr - fp->_IO_write_base;
     716  	  /* For all other modes, _IO_read_end represents the file offset.  */
     717  	  else
     718  	    offset += fp->_IO_write_ptr - fp->_IO_read_end;
     719  	}
     720      }
     721  
     722    if (fp->_offset != _IO_pos_BAD)
     723      result = fp->_offset;
     724    else
     725      result = _IO_SYSSEEK (fp, 0, _IO_seek_cur);
     726  
     727    if (result == EOF)
     728      return result;
     729  
     730    result += offset;
     731  
     732    if (result < 0)
     733      {
     734        __set_errno (EINVAL);
     735        return EOF;
     736      }
     737  
     738    return result;
     739  }
     740  
     741  off64_t
     742  _IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
     743  {
     744    off64_t result;
     745    off64_t delta, new_offset;
     746    long int count;
     747  
     748    /* Short-circuit into a separate function.  We don't want to mix any
     749       functionality and we don't want to touch anything inside the FILE
     750       object. */
     751    if (mode == 0)
     752      return do_ftell_wide (fp);
     753  
     754    /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
     755       offset of the underlying file must be exact.  */
     756    int must_be_exact = ((fp->_wide_data->_IO_read_base
     757  			== fp->_wide_data->_IO_read_end)
     758  		       && (fp->_wide_data->_IO_write_base
     759  			   == fp->_wide_data->_IO_write_ptr));
     760  
     761    bool was_writing = ((fp->_wide_data->_IO_write_ptr
     762  		       > fp->_wide_data->_IO_write_base)
     763  		      || _IO_in_put_mode (fp));
     764  
     765    /* Flush unwritten characters.
     766       (This may do an unneeded write if we seek within the buffer.
     767       But to be able to switch to reading, we would need to set
     768       egptr to pptr.  That can't be done in the current design,
     769       which assumes file_ptr() is eGptr.  Anyway, since we probably
     770       end up flushing when we close(), it doesn't make much difference.)
     771       FIXME: simulate mem-mapped files. */
     772    if (was_writing && _IO_switch_to_wget_mode (fp))
     773      return WEOF;
     774  
     775    if (fp->_wide_data->_IO_buf_base == NULL)
     776      {
     777        /* It could be that we already have a pushback buffer.  */
     778        if (fp->_wide_data->_IO_read_base != NULL)
     779  	{
     780  	  free (fp->_wide_data->_IO_read_base);
     781  	  fp->_flags &= ~_IO_IN_BACKUP;
     782  	}
     783        _IO_doallocbuf (fp);
     784        _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
     785        _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
     786        _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
     787  		 fp->_wide_data->_IO_buf_base);
     788        _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
     789  		 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
     790      }
     791  
     792    switch (dir)
     793      {
     794        struct _IO_codecvt *cv;
     795        int clen;
     796  
     797      case _IO_seek_cur:
     798        /* Adjust for read-ahead (bytes is buffer).  To do this we must
     799  	 find out which position in the external buffer corresponds to
     800  	 the current position in the internal buffer.  */
     801        cv = fp->_codecvt;
     802        clen = __libio_codecvt_encoding (cv);
     803  
     804        if (mode != 0 || !was_writing)
     805  	{
     806  	  if (clen > 0)
     807  	    {
     808  	      offset -= (fp->_wide_data->_IO_read_end
     809  			 - fp->_wide_data->_IO_read_ptr) * clen;
     810  	      /* Adjust by readahead in external buffer.  */
     811  	      offset -= fp->_IO_read_end - fp->_IO_read_ptr;
     812  	    }
     813  	  else
     814  	    {
     815  	      int nread;
     816  
     817  	      delta = (fp->_wide_data->_IO_read_ptr
     818  		       - fp->_wide_data->_IO_read_base);
     819  	      fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
     820  	      nread = __libio_codecvt_length (cv,
     821  					      &fp->_wide_data->_IO_state,
     822  					      fp->_IO_read_base,
     823  					      fp->_IO_read_end, delta);
     824  	      fp->_IO_read_ptr = fp->_IO_read_base + nread;
     825  	      fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
     826  	      offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
     827  	    }
     828  	}
     829  
     830        if (fp->_offset == _IO_pos_BAD)
     831  	goto dumb;
     832  
     833        /* Make offset absolute, assuming current pointer is file_ptr(). */
     834        offset += fp->_offset;
     835  
     836        dir = _IO_seek_set;
     837        break;
     838      case _IO_seek_set:
     839        break;
     840      case _IO_seek_end:
     841        {
     842  	struct __stat64_t64 st;
     843  	if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode))
     844  	  {
     845  	    offset += st.st_size;
     846  	    dir = _IO_seek_set;
     847  	  }
     848  	else
     849  	  goto dumb;
     850        }
     851      }
     852  
     853    _IO_free_wbackup_area (fp);
     854  
     855    /* At this point, dir==_IO_seek_set. */
     856  
     857    /* If destination is within current buffer, optimize: */
     858    if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
     859        && !_IO_in_backup (fp))
     860      {
     861        off64_t start_offset = (fp->_offset
     862                                - (fp->_IO_read_end - fp->_IO_buf_base));
     863        if (offset >= start_offset && offset < fp->_offset)
     864  	{
     865  	  _IO_setg (fp, fp->_IO_buf_base,
     866  		    fp->_IO_buf_base + (offset - start_offset),
     867  		    fp->_IO_read_end);
     868  	  _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
     869  	  _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
     870  		     fp->_wide_data->_IO_buf_base,
     871  		     fp->_wide_data->_IO_buf_base);
     872  	  _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
     873  		     fp->_wide_data->_IO_buf_base);
     874  
     875  	  if (adjust_wide_data (fp, false))
     876  	    goto dumb;
     877  
     878  	  _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
     879  	  goto resync;
     880  	}
     881      }
     882  
     883    if (fp->_flags & _IO_NO_READS)
     884      goto dumb;
     885  
     886    /* Try to seek to a block boundary, to improve kernel page management. */
     887    new_offset = offset & ~(fp->_IO_buf_end - fp->_IO_buf_base - 1);
     888    delta = offset - new_offset;
     889    if (delta > fp->_IO_buf_end - fp->_IO_buf_base)
     890      {
     891        new_offset = offset;
     892        delta = 0;
     893      }
     894    result = _IO_SYSSEEK (fp, new_offset, 0);
     895    if (result < 0)
     896      return EOF;
     897    if (delta == 0)
     898      count = 0;
     899    else
     900      {
     901        count = _IO_SYSREAD (fp, fp->_IO_buf_base,
     902  			   (must_be_exact
     903  			    ? delta : fp->_IO_buf_end - fp->_IO_buf_base));
     904        if (count < delta)
     905  	{
     906  	  /* We weren't allowed to read, but try to seek the remainder. */
     907  	  offset = count == EOF ? delta : delta-count;
     908  	  dir = _IO_seek_cur;
     909  	  goto dumb;
     910  	}
     911      }
     912    _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + delta,
     913  	    fp->_IO_buf_base + count);
     914    _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
     915    _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
     916  	     fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
     917    _IO_wsetp (fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
     918  
     919    if (adjust_wide_data (fp, true))
     920      goto dumb;
     921  
     922    fp->_offset = result + count;
     923    _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
     924    return offset;
     925   dumb:
     926  
     927    _IO_unsave_markers (fp);
     928    result = _IO_SYSSEEK (fp, offset, dir);
     929    if (result != EOF)
     930      {
     931        _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
     932        fp->_offset = result;
     933        _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
     934        _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
     935        _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
     936  		 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
     937        _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
     938  		 fp->_wide_data->_IO_buf_base);
     939      }
     940    return result;
     941  
     942  resync:
     943    /* We need to do it since it is possible that the file offset in
     944       the kernel may be changed behind our back. It may happen when
     945       we fopen a file and then do a fork. One process may access the
     946       file and the kernel file offset will be changed. */
     947    if (fp->_offset >= 0)
     948      _IO_SYSSEEK (fp, fp->_offset, 0);
     949  
     950    return offset;
     951  }
     952  libc_hidden_def (_IO_wfile_seekoff)
     953  
     954  
     955  size_t
     956  _IO_wfile_xsputn (FILE *f, const void *data, size_t n)
     957  {
     958    const wchar_t *s = (const wchar_t *) data;
     959    size_t to_do = n;
     960    int must_flush = 0;
     961    size_t count;
     962  
     963    if (n <= 0)
     964      return 0;
     965    /* This is an optimized implementation.
     966       If the amount to be written straddles a block boundary
     967       (or the filebuf is unbuffered), use sys_write directly. */
     968  
     969    /* First figure out how much space is available in the buffer. */
     970    count = f->_wide_data->_IO_write_end - f->_wide_data->_IO_write_ptr;
     971    if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
     972      {
     973        count = f->_wide_data->_IO_buf_end - f->_wide_data->_IO_write_ptr;
     974        if (count >= n)
     975  	{
     976  	  const wchar_t *p;
     977  	  for (p = s + n; p > s; )
     978  	    {
     979  	      if (*--p == L'\n')
     980  		{
     981  		  count = p - s + 1;
     982  		  must_flush = 1;
     983  		  break;
     984  		}
     985  	    }
     986  	}
     987      }
     988    /* Then fill the buffer. */
     989    if (count > 0)
     990      {
     991        if (count > to_do)
     992  	count = to_do;
     993        if (count > 20)
     994  	{
     995  	  f->_wide_data->_IO_write_ptr =
     996  	    __wmempcpy (f->_wide_data->_IO_write_ptr, s, count);
     997  	  s += count;
     998  	}
     999        else
    1000  	{
    1001  	  wchar_t *p = f->_wide_data->_IO_write_ptr;
    1002  	  int i = (int) count;
    1003  	  while (--i >= 0)
    1004  	    *p++ = *s++;
    1005  	  f->_wide_data->_IO_write_ptr = p;
    1006  	}
    1007        to_do -= count;
    1008      }
    1009    if (to_do > 0)
    1010      to_do -= _IO_wdefault_xsputn (f, s, to_do);
    1011    if (must_flush
    1012        && f->_wide_data->_IO_write_ptr != f->_wide_data->_IO_write_base)
    1013      _IO_wdo_write (f, f->_wide_data->_IO_write_base,
    1014  		   f->_wide_data->_IO_write_ptr
    1015  		   - f->_wide_data->_IO_write_base);
    1016  
    1017    return n - to_do;
    1018  }
    1019  libc_hidden_def (_IO_wfile_xsputn)