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