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