xref: /haiku/src/system/libroot/posix/glibc/libio/wfileops.c (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
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 #ifdef HAVE_MMAP
296 static 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 
358 static 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 
372 wint_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 }
444 INTDEF(_IO_wfile_overflow)
445 
446 wint_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 }
506 INTDEF(_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_read_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_read_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 
773 resync:
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 }
783 INTDEF(_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 }
858 INTDEF(_IO_wfile_xsputn)
859 
860 
861 struct _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 };
884 INTVARDEF(_IO_wfile_jumps)
885 
886 
887 #ifdef HAVE_MMAP
888 struct _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 
912 struct _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