xref: /haiku/src/system/libroot/posix/glibc/libio/wfileops.c (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
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. */
57 int
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 }
116 INTDEF(_IO_wdo_write)
117 
118 
119 wint_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 }
292 INTDEF(_IO_wfile_underflow)
293 
294 
295 static wint_t
296 _IO_wfile_underflow_mmap (_IO_FILE *fp)
297 {
298   struct _IO_codecvt *cd;
299   enum __codecvt_result status;
300   const char *read_stop;
301 
302   if (__builtin_expect (fp->_flags & _IO_NO_READS, 0))
303     {
304       fp->_flags |= _IO_ERR_SEEN;
305       __set_errno (EBADF);
306       return WEOF;
307     }
308   if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
309     return *fp->_wide_data->_IO_read_ptr;
310 
311   cd = fp->_codecvt;
312 
313   /* Maybe there is something left in the external buffer.  */
314   if (fp->_IO_read_ptr >= fp->_IO_read_end
315       /* No.  But maybe the read buffer is not fully set up.  */
316       && _IO_file_underflow_mmap (fp) == EOF)
317     /* Nothing available.  _IO_file_underflow_mmap has set the EOF or error
318        flags as appropriate.  */
319     return WEOF;
320 
321   /* There is more in the external.  Convert it.  */
322   read_stop = (const char *) fp->_IO_read_ptr;
323 
324   if (fp->_wide_data->_IO_buf_base == NULL)
325     {
326       /* Maybe we already have a push back pointer.  */
327       if (fp->_wide_data->_IO_save_base != NULL)
328 	{
329 	  free (fp->_wide_data->_IO_save_base);
330 	  fp->_flags &= ~_IO_IN_BACKUP;
331 	}
332       INTUSE(_IO_wdoallocbuf) (fp);
333     }
334 
335   fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
336   fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
337     fp->_wide_data->_IO_buf_base;
338   status = (*cd->__codecvt_do_in) (cd, &fp->_wide_data->_IO_state,
339 				   fp->_IO_read_ptr, fp->_IO_read_end,
340 				   &read_stop,
341 				   fp->_wide_data->_IO_read_ptr,
342 				   fp->_wide_data->_IO_buf_end,
343 				   &fp->_wide_data->_IO_read_end);
344 
345   fp->_IO_read_ptr = (char *) read_stop;
346 
347   /* If we managed to generate some text return the next character.  */
348   if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
349     return *fp->_wide_data->_IO_read_ptr;
350 
351   /* There is some garbage at the end of the file.  */
352   __set_errno (EILSEQ);
353   fp->_flags |= _IO_ERR_SEEN;
354   return WEOF;
355 }
356 
357 static wint_t
358 _IO_wfile_underflow_maybe_mmap (_IO_FILE *fp)
359 {
360   /* This is the first read attempt.  Doing the underflow will choose mmap
361      or vanilla operations and then punt to the chosen underflow routine.
362      Then we can punt to ours.  */
363   if (_IO_file_underflow_maybe_mmap (fp) == EOF)
364     return WEOF;
365 
366   return _IO_WUNDERFLOW (fp);
367 }
368 
369 
370 wint_t
371 _IO_wfile_overflow (f, wch)
372      _IO_FILE *f;
373      wint_t wch;
374 {
375   if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
376     {
377       f->_flags |= _IO_ERR_SEEN;
378       __set_errno (EBADF);
379       return WEOF;
380     }
381   /* If currently reading or no buffer allocated. */
382   if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0)
383     {
384       /* Allocate a buffer if needed. */
385       if (f->_wide_data->_IO_write_base == 0)
386 	{
387 	  INTUSE(_IO_wdoallocbuf) (f);
388 	  _IO_wsetg (f, f->_wide_data->_IO_buf_base,
389 		     f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);
390 
391 	  if (f->_IO_write_base == NULL)
392 	    {
393 	      INTUSE(_IO_doallocbuf) (f);
394 	      _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
395 	    }
396 	}
397       else
398 	{
399 	  /* Otherwise must be currently reading.  If _IO_read_ptr
400 	     (and hence also _IO_read_end) is at the buffer end,
401 	     logically slide the buffer forwards one block (by setting
402 	     the read pointers to all point at the beginning of the
403 	     block).  This makes room for subsequent output.
404 	     Otherwise, set the read pointers to _IO_read_end (leaving
405 	     that alone, so it can continue to correspond to the
406 	     external position). */
407 	  if (f->_wide_data->_IO_read_ptr == f->_wide_data->_IO_buf_end)
408 	    {
409 	      f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
410 	      f->_wide_data->_IO_read_end = f->_wide_data->_IO_read_ptr =
411 		f->_wide_data->_IO_buf_base;
412 	    }
413 	}
414       f->_wide_data->_IO_write_ptr = f->_wide_data->_IO_read_ptr;
415       f->_wide_data->_IO_write_base = f->_wide_data->_IO_write_ptr;
416       f->_wide_data->_IO_write_end = f->_wide_data->_IO_buf_end;
417       f->_wide_data->_IO_read_base = f->_wide_data->_IO_read_ptr =
418 	f->_wide_data->_IO_read_end;
419 
420       f->_IO_write_ptr = f->_IO_read_ptr;
421       f->_IO_write_base = f->_IO_write_ptr;
422       f->_IO_write_end = f->_IO_buf_end;
423       f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
424 
425       f->_flags |= _IO_CURRENTLY_PUTTING;
426       if (f->_flags & (_IO_LINE_BUF+_IO_UNBUFFERED))
427 	f->_wide_data->_IO_write_end = f->_wide_data->_IO_write_ptr;
428     }
429   if (wch == WEOF)
430     return _IO_do_flush (f);
431   if (f->_wide_data->_IO_write_ptr == f->_wide_data->_IO_buf_end)
432     /* Buffer is really full */
433     if (_IO_do_flush (f) == EOF)
434       return WEOF;
435   *f->_wide_data->_IO_write_ptr++ = wch;
436   if ((f->_flags & _IO_UNBUFFERED)
437       || ((f->_flags & _IO_LINE_BUF) && wch == L'\n'))
438     if (_IO_do_flush (f) == EOF)
439       return WEOF;
440   return wch;
441 }
442 INTDEF(_IO_wfile_overflow)
443 
444 wint_t
445 _IO_wfile_sync (fp)
446      _IO_FILE *fp;
447 {
448   _IO_ssize_t delta;
449   wint_t retval = 0;
450 
451   /*    char* ptr = cur_ptr(); */
452   if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
453     if (_IO_do_flush (fp))
454       return WEOF;
455   delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_end;
456   if (delta != 0)
457     {
458       /* We have to find out how many bytes we have to go back in the
459 	 external buffer.  */
460       struct _IO_codecvt *cv = fp->_codecvt;
461       _IO_off64_t new_pos;
462 
463       int clen = (*cv->__codecvt_do_encoding) (cv);
464 
465       if (clen > 0)
466 	/* It is easy, a fixed number of input bytes are used for each
467 	   wide character.  */
468 	delta *= clen;
469       else
470 	{
471 	  /* We have to find out the hard way how much to back off.
472              To do this we determine how much input we needed to
473              generate the wide characters up to the current reading
474              position.  */
475 	  int nread;
476 
477 	  fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
478 	  nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
479 					      fp->_IO_read_base,
480 					      fp->_IO_read_end, delta);
481 	  fp->_IO_read_ptr = fp->_IO_read_base + nread;
482 	  delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
483 	}
484 
485       new_pos = _IO_SYSSEEK (fp, delta, 1);
486       if (new_pos != (_IO_off64_t) EOF)
487 	{
488 	  fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
489 	  fp->_IO_read_end = fp->_IO_read_ptr;
490 	}
491 #ifdef ESPIPE
492       else if (errno == ESPIPE)
493 	; /* Ignore error from unseekable devices. */
494 #endif
495       else
496 	retval = WEOF;
497     }
498   if (retval != WEOF)
499     fp->_offset = _IO_pos_BAD;
500   /* FIXME: Cleanup - can this be shared? */
501   /*    setg(base(), ptr, ptr); */
502   return retval;
503 }
504 INTDEF(_IO_wfile_sync)
505 
506 _IO_off64_t
507 _IO_wfile_seekoff (fp, offset, dir, mode)
508      _IO_FILE *fp;
509      _IO_off64_t offset;
510      int dir;
511      int mode;
512 {
513   _IO_off64_t result;
514   _IO_off64_t delta, new_offset;
515   long int count;
516   /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
517      offset of the underlying file must be exact.  */
518   int must_be_exact = ((fp->_wide_data->_IO_read_base
519 			== fp->_wide_data->_IO_read_end)
520 		       && (fp->_wide_data->_IO_write_base
521 			   == fp->_wide_data->_IO_write_ptr));
522 
523   if (mode == 0)
524     {
525       /* XXX For wide stream with backup store it is not very
526 	 reasonable to determine the offset.  The pushed-back
527 	 character might require a state change and we need not be
528 	 able to compute the initial state by reverse transformation
529 	 since there is no guarantee of symmetry.  So we don't even
530 	 try and return an error.  */
531       if (_IO_in_backup (fp))
532 	{
533 	  if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
534 	    {
535 	      __set_errno (EINVAL);
536 	      return -1;
537 	    }
538 
539 	  /* There is no more data in the backup buffer.  We can
540 	     switch back.  */
541 	  INTUSE(_IO_switch_to_main_wget_area) (fp);
542 	}
543 
544       dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
545     }
546 
547   /* Flush unwritten characters.
548      (This may do an unneeded write if we seek within the buffer.
549      But to be able to switch to reading, we would need to set
550      egptr to ptr.  That can't be done in the current design,
551      which assumes file_ptr() is eGptr.  Anyway, since we probably
552      end up flushing when we close(), it doesn't make much difference.)
553      FIXME: simulate mem-mapped files. */
554 
555   if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
556       || _IO_in_put_mode (fp))
557     if (INTUSE(_IO_switch_to_wget_mode) (fp))
558       return WEOF;
559 
560   if (fp->_wide_data->_IO_buf_base == NULL)
561     {
562       /* It could be that we already have a pushback buffer.  */
563       if (fp->_wide_data->_IO_read_base != NULL)
564 	{
565 	  free (fp->_wide_data->_IO_read_base);
566 	  fp->_flags &= ~_IO_IN_BACKUP;
567 	}
568       INTUSE(_IO_doallocbuf) (fp);
569       _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
570       _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
571       _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
572 		 fp->_wide_data->_IO_buf_base);
573       _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
574 		 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
575     }
576 
577   switch (dir)
578     {
579       struct _IO_codecvt *cv;
580       int clen;
581 
582     case _IO_seek_cur:
583       /* Adjust for read-ahead (bytes is buffer).  To do this we must
584          find out which position in the external buffer corresponds to
585          the current position in the internal buffer.  */
586       cv = fp->_codecvt;
587       clen = (*cv->__codecvt_do_encoding) (cv);
588 
589       if (clen > 0)
590 	offset -= (fp->_wide_data->_IO_read_end
591 		   - fp->_wide_data->_IO_read_ptr) * clen;
592       else
593 	{
594 	  int nread;
595 
596 	  delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_base;
597 	  fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
598 	  nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
599 					      fp->_IO_read_base,
600 					      fp->_IO_read_end, delta);
601 	  fp->_IO_read_ptr = fp->_IO_read_base + nread;
602 	  fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
603 	  offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
604 	}
605 
606       if (fp->_offset == _IO_pos_BAD)
607 	goto dumb;
608       /* Make offset absolute, assuming current pointer is file_ptr(). */
609       offset += fp->_offset;
610 
611       dir = _IO_seek_set;
612       break;
613     case _IO_seek_set:
614       break;
615     case _IO_seek_end:
616       {
617 	struct _G_stat64 st;
618 	if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode))
619 	  {
620 	    offset += st.st_size;
621 	    dir = _IO_seek_set;
622 	  }
623 	else
624 	  goto dumb;
625       }
626     }
627   /* At this point, dir==_IO_seek_set. */
628 
629   /* If we are only interested in the current position we've found it now.  */
630   if (mode == 0)
631     return offset;
632 
633   /* If destination is within current buffer, optimize: */
634   if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
635       && !_IO_in_backup (fp))
636     {
637       /* Offset relative to start of main get area. */
638       _IO_off64_t rel_offset = (offset - fp->_offset
639 				+ (fp->_IO_read_end - fp->_IO_read_base));
640       if (rel_offset >= 0)
641 	{
642 #if 0
643 	  if (_IO_in_backup (fp))
644 	    _IO_switch_to_main_get_area (fp);
645 #endif
646 	  if (rel_offset <= fp->_IO_read_end - fp->_IO_read_base)
647 	    {
648 	      enum __codecvt_result status;
649 	      struct _IO_codecvt *cd = fp->_codecvt;
650 	      const char *read_ptr_copy;
651 
652 	      fp->_IO_read_ptr = fp->_IO_read_base + rel_offset;
653 	      _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
654 
655 	      /* Now set the pointer for the internal buffer.  This
656                  might be an iterative process.  Though the read
657                  pointer is somewhere in the current external buffer
658                  this does not mean we can convert this whole buffer
659                  at once fitting in the internal buffer.  */
660 	      fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
661 	      read_ptr_copy = fp->_IO_read_base;
662 	      fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_base;
663 	      do
664 		{
665 		  wchar_t buffer[1024];
666 		  wchar_t *ignore;
667 		  status = (*cd->__codecvt_do_in) (cd,
668 						   &fp->_wide_data->_IO_state,
669 						   read_ptr_copy,
670 						   fp->_IO_read_ptr,
671 						   &read_ptr_copy,
672 						   buffer,
673 						   buffer
674 						   + (sizeof (buffer)
675 						      / sizeof (buffer[0])),
676 						   &ignore);
677 		  if (status != __codecvt_ok && status != __codecvt_partial)
678 		    {
679 		      fp->_flags |= _IO_ERR_SEEN;
680 		      goto dumb;
681 		    }
682 		}
683 	      while (read_ptr_copy != fp->_IO_read_ptr);
684 
685 	      fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_base;
686 
687 	      _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
688 	      goto resync;
689 	    }
690 #ifdef TODO
691 	    /* If we have streammarkers, seek forward by reading ahead. */
692 	    if (_IO_have_markers (fp))
693 	      {
694 		int to_skip = rel_offset
695 		  - (fp->_IO_read_ptr - fp->_IO_read_base);
696 		if (ignore (to_skip) != to_skip)
697 		  goto dumb;
698 		_IO_mask_flags (fp, 0, _IO_EOF_SEEN);
699 		goto resync;
700 	      }
701 #endif
702 	}
703 #ifdef TODO
704       if (rel_offset < 0 && rel_offset >= Bbase () - Bptr ())
705 	{
706 	  if (!_IO_in_backup (fp))
707 	    _IO_switch_to_backup_area (fp);
708 	  gbump (fp->_IO_read_end + rel_offset - fp->_IO_read_ptr);
709 	  _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
710 	  goto resync;
711 	}
712 #endif
713     }
714 
715 #ifdef TODO
716   INTUSE(_IO_unsave_markers) (fp);
717 #endif
718 
719   if (fp->_flags & _IO_NO_READS)
720     goto dumb;
721 
722   /* Try to seek to a block boundary, to improve kernel page management. */
723   new_offset = offset & ~(fp->_IO_buf_end - fp->_IO_buf_base - 1);
724   delta = offset - new_offset;
725   if (delta > fp->_IO_buf_end - fp->_IO_buf_base)
726     {
727       new_offset = offset;
728       delta = 0;
729     }
730   result = _IO_SYSSEEK (fp, new_offset, 0);
731   if (result < 0)
732     return EOF;
733   if (delta == 0)
734     count = 0;
735   else
736     {
737       count = _IO_SYSREAD (fp, fp->_IO_buf_base,
738 			   (must_be_exact
739 			    ? delta : fp->_IO_buf_end - fp->_IO_buf_base));
740       if (count < delta)
741 	{
742 	  /* We weren't allowed to read, but try to seek the remainder. */
743 	  offset = count == EOF ? delta : delta-count;
744 	  dir = _IO_seek_cur;
745 	  goto dumb;
746 	}
747     }
748   _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + delta,
749 	    fp->_IO_buf_base + count);
750   _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
751   fp->_offset = result + count;
752   _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
753   return offset;
754  dumb:
755 
756   INTUSE(_IO_unsave_markers) (fp);
757   result = _IO_SYSSEEK (fp, offset, dir);
758   if (result != EOF)
759     {
760       _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
761       fp->_offset = result;
762       _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
763       _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
764       _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
765 		 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
766       _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
767 		 fp->_wide_data->_IO_buf_base);
768     }
769   return result;
770 
771 resync:
772   /* We need to do it since it is possible that the file offset in
773      the kernel may be changed behind our back. It may happen when
774      we fopen a file and then do a fork. One process may access the
775      the file and the kernel file offset will be changed. */
776   if (fp->_offset >= 0)
777     _IO_SYSSEEK (fp, fp->_offset, 0);
778 
779   return offset;
780 }
781 INTDEF(_IO_wfile_seekoff)
782 
783 
784 _IO_size_t
785 _IO_wfile_xsputn (f, data, n)
786      _IO_FILE *f;
787      const void *data;
788      _IO_size_t n;
789 {
790   register const wchar_t *s = (const wchar_t *) data;
791   _IO_size_t to_do = n;
792   int must_flush = 0;
793   _IO_size_t count;
794 
795   if (n <= 0)
796     return 0;
797   /* This is an optimized implementation.
798      If the amount to be written straddles a block boundary
799      (or the filebuf is unbuffered), use sys_write directly. */
800 
801   /* First figure out how much space is available in the buffer. */
802   count = f->_wide_data->_IO_write_end - f->_wide_data->_IO_write_ptr;
803   if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
804     {
805       count = f->_wide_data->_IO_buf_end - f->_wide_data->_IO_write_ptr;
806       if (count >= n)
807 	{
808 	  register const wchar_t *p;
809 	  for (p = s + n; p > s; )
810 	    {
811 	      if (*--p == L'\n')
812 		{
813 		  count = p - s + 1;
814 		  must_flush = 1;
815 		  break;
816 		}
817 	    }
818 	}
819     }
820   /* Then fill the buffer. */
821   if (count > 0)
822     {
823       if (count > to_do)
824 	count = to_do;
825       if (count > 20)
826 	{
827 #ifdef _LIBC
828 	  f->_wide_data->_IO_write_ptr =
829 	    __wmempcpy (f->_wide_data->_IO_write_ptr, s, count);
830 #else
831 	  wmemcpy (f->_wide_data->_IO_write_ptr, s, count);
832 	  f->_wide_data->_IO_write_ptr += count;
833 #endif
834 	  s += count;
835 	}
836       else
837 	{
838 	  register wchar_t *p = f->_wide_data->_IO_write_ptr;
839 	  register int i = (int) count;
840 	  while (--i >= 0)
841 	    *p++ = *s++;
842 	  f->_wide_data->_IO_write_ptr = p;
843 	}
844       to_do -= count;
845     }
846   if (to_do > 0)
847     to_do -= INTUSE(_IO_wdefault_xsputn) (f, s, to_do);
848   if (must_flush
849       && f->_wide_data->_IO_write_ptr != f->_wide_data->_IO_write_base)
850     INTUSE(_IO_wdo_write) (f, f->_wide_data->_IO_write_base,
851 			   f->_wide_data->_IO_write_ptr
852 			   - f->_wide_data->_IO_write_base);
853 
854   return n - to_do;
855 }
856 INTDEF(_IO_wfile_xsputn)
857 
858 
859 struct _IO_jump_t _IO_wfile_jumps =
860 {
861   JUMP_INIT_DUMMY,
862   JUMP_INIT(finish, _IO_new_file_finish),
863   JUMP_INIT(overflow, (_IO_overflow_t) INTUSE(_IO_wfile_overflow)),
864   JUMP_INIT(underflow, (_IO_underflow_t) INTUSE(_IO_wfile_underflow)),
865   JUMP_INIT(uflow, (_IO_underflow_t) INTUSE(_IO_wdefault_uflow)),
866   JUMP_INIT(pbackfail, (_IO_pbackfail_t) INTUSE(_IO_wdefault_pbackfail)),
867   JUMP_INIT(xsputn, INTUSE(_IO_wfile_xsputn)),
868   JUMP_INIT(xsgetn, INTUSE(_IO_file_xsgetn)),
869   JUMP_INIT(seekoff, INTUSE(_IO_wfile_seekoff)),
870   JUMP_INIT(seekpos, _IO_default_seekpos),
871   JUMP_INIT(setbuf, _IO_new_file_setbuf),
872   JUMP_INIT(sync, (_IO_sync_t) INTUSE(_IO_wfile_sync)),
873   JUMP_INIT(doallocate, _IO_wfile_doallocate),
874   JUMP_INIT(read, INTUSE(_IO_file_read)),
875   JUMP_INIT(write, _IO_new_file_write),
876   JUMP_INIT(seek, INTUSE(_IO_file_seek)),
877   JUMP_INIT(close, INTUSE(_IO_file_close)),
878   JUMP_INIT(stat, INTUSE(_IO_file_stat)),
879   JUMP_INIT(showmanyc, _IO_default_showmanyc),
880   JUMP_INIT(imbue, _IO_default_imbue)
881 };
882 INTVARDEF(_IO_wfile_jumps)
883 
884 
885 struct _IO_jump_t _IO_wfile_jumps_mmap =
886 {
887   JUMP_INIT_DUMMY,
888   JUMP_INIT(finish, _IO_new_file_finish),
889   JUMP_INIT(overflow, (_IO_overflow_t) INTUSE(_IO_wfile_overflow)),
890   JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_mmap),
891   JUMP_INIT(uflow, (_IO_underflow_t) INTUSE(_IO_wdefault_uflow)),
892   JUMP_INIT(pbackfail, (_IO_pbackfail_t) INTUSE(_IO_wdefault_pbackfail)),
893   JUMP_INIT(xsputn, INTUSE(_IO_wfile_xsputn)),
894   JUMP_INIT(xsgetn, INTUSE(_IO_file_xsgetn)),
895   JUMP_INIT(seekoff, INTUSE(_IO_wfile_seekoff)),
896   JUMP_INIT(seekpos, _IO_default_seekpos),
897   JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
898   JUMP_INIT(sync, (_IO_sync_t) INTUSE(_IO_wfile_sync)),
899   JUMP_INIT(doallocate, _IO_wfile_doallocate),
900   JUMP_INIT(read, INTUSE(_IO_file_read)),
901   JUMP_INIT(write, _IO_new_file_write),
902   JUMP_INIT(seek, INTUSE(_IO_file_seek)),
903   JUMP_INIT(close, _IO_file_close_mmap),
904   JUMP_INIT(stat, INTUSE(_IO_file_stat)),
905   JUMP_INIT(showmanyc, _IO_default_showmanyc),
906   JUMP_INIT(imbue, _IO_default_imbue)
907 };
908 
909 struct _IO_jump_t _IO_wfile_jumps_maybe_mmap =
910 {
911   JUMP_INIT_DUMMY,
912   JUMP_INIT(finish, _IO_new_file_finish),
913   JUMP_INIT(overflow, (_IO_overflow_t) INTUSE(_IO_wfile_overflow)),
914   JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_maybe_mmap),
915   JUMP_INIT(uflow, (_IO_underflow_t) INTUSE(_IO_wdefault_uflow)),
916   JUMP_INIT(pbackfail, (_IO_pbackfail_t) INTUSE(_IO_wdefault_pbackfail)),
917   JUMP_INIT(xsputn, INTUSE(_IO_wfile_xsputn)),
918   JUMP_INIT(xsgetn, INTUSE(_IO_file_xsgetn)),
919   JUMP_INIT(seekoff, INTUSE(_IO_wfile_seekoff)),
920   JUMP_INIT(seekpos, _IO_default_seekpos),
921   JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
922   JUMP_INIT(sync, (_IO_sync_t) INTUSE(_IO_wfile_sync)),
923   JUMP_INIT(doallocate, _IO_wfile_doallocate),
924   JUMP_INIT(read, INTUSE(_IO_file_read)),
925   JUMP_INIT(write, _IO_new_file_write),
926   JUMP_INIT(seek, INTUSE(_IO_file_seek)),
927   JUMP_INIT(close, INTUSE(_IO_file_close)),
928   JUMP_INIT(stat, INTUSE(_IO_file_stat)),
929   JUMP_INIT(showmanyc, _IO_default_showmanyc),
930   JUMP_INIT(imbue, _IO_default_imbue)
931 };
932