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