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