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. */
36int
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}
107libc_hidden_def (_IO_wdo_write)
108
109
110wint_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 (ptr: 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 (ptr: 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}
328libc_hidden_def (_IO_wfile_underflow)
329
330
331wint_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 (ptr: 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
392wint_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
405wint_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}
477libc_hidden_def (_IO_wfile_overflow)
478
479wint_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}
537libc_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. */
545static int
546adjust_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
583done:
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. */
593static off64_t
594do_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 (size: 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 (ptr: out);
702 return WEOF;
703 }
704
705 offset += outstop - out;
706 free (ptr: 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
741off64_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 (ptr: 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
942resync:
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}
952libc_hidden_def (_IO_wfile_seekoff)
953
954
955size_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 (s1: f->_wide_data->_IO_write_ptr, s2: s, n: 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 (fp: f, data: f->_wide_data->_IO_write_base,
1014 to_do: f->_wide_data->_IO_write_ptr
1015 - f->_wide_data->_IO_write_base);
1016
1017 return n - to_do;
1018}
1019libc_hidden_def (_IO_wfile_xsputn)
1020

source code of glibc/libio/wfileops.c