1/* Copyright (C) 1993-2022 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_write_ptr - fp->_IO_write_base < 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
331static 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 (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
392static 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
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 {
417 /* Allocate a buffer if needed. */
418 if (f->_wide_data->_IO_write_base == 0)
419 {
420 _IO_wdoallocbuf (f);
421 _IO_free_wbackup_area (f);
422 _IO_wsetg (f, f->_wide_data->_IO_buf_base,
423 f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);
424
425 if (f->_IO_write_base == NULL)
426 {
427 _IO_doallocbuf (f);
428 _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
429 }
430 }
431 else
432 {
433 /* Otherwise must be currently reading. If _IO_read_ptr
434 (and hence also _IO_read_end) is at the buffer end,
435 logically slide the buffer forwards one block (by setting
436 the read pointers to all point at the beginning of the
437 block). This makes room for subsequent output.
438 Otherwise, set the read pointers to _IO_read_end (leaving
439 that alone, so it can continue to correspond to the
440 external position). */
441 if (f->_wide_data->_IO_read_ptr == f->_wide_data->_IO_buf_end)
442 {
443 f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
444 f->_wide_data->_IO_read_end = f->_wide_data->_IO_read_ptr =
445 f->_wide_data->_IO_buf_base;
446 }
447 }
448 f->_wide_data->_IO_write_ptr = f->_wide_data->_IO_read_ptr;
449 f->_wide_data->_IO_write_base = f->_wide_data->_IO_write_ptr;
450 f->_wide_data->_IO_write_end = f->_wide_data->_IO_buf_end;
451 f->_wide_data->_IO_read_base = f->_wide_data->_IO_read_ptr =
452 f->_wide_data->_IO_read_end;
453
454 f->_IO_write_ptr = f->_IO_read_ptr;
455 f->_IO_write_base = f->_IO_write_ptr;
456 f->_IO_write_end = f->_IO_buf_end;
457 f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
458
459 f->_flags |= _IO_CURRENTLY_PUTTING;
460 if (f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
461 f->_wide_data->_IO_write_end = f->_wide_data->_IO_write_ptr;
462 }
463 if (wch == WEOF)
464 return _IO_do_flush (f);
465 if (f->_wide_data->_IO_write_ptr == f->_wide_data->_IO_buf_end)
466 /* Buffer is really full */
467 if (_IO_do_flush (f) == EOF)
468 return WEOF;
469 *f->_wide_data->_IO_write_ptr++ = wch;
470 if ((f->_flags & _IO_UNBUFFERED)
471 || ((f->_flags & _IO_LINE_BUF) && wch == L'\n'))
472 if (_IO_do_flush (f) == EOF)
473 return WEOF;
474 return wch;
475}
476libc_hidden_def (_IO_wfile_overflow)
477
478wint_t
479_IO_wfile_sync (FILE *fp)
480{
481 ssize_t delta;
482 wint_t retval = 0;
483
484 /* char* ptr = cur_ptr(); */
485 if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
486 if (_IO_do_flush (fp))
487 return WEOF;
488 delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_end;
489 if (delta != 0)
490 {
491 /* We have to find out how many bytes we have to go back in the
492 external buffer. */
493 struct _IO_codecvt *cv = fp->_codecvt;
494 off64_t new_pos;
495
496 int clen = __libio_codecvt_encoding (cv);
497
498 if (clen > 0)
499 /* It is easy, a fixed number of input bytes are used for each
500 wide character. */
501 delta *= clen;
502 else
503 {
504 /* We have to find out the hard way how much to back off.
505 To do this we determine how much input we needed to
506 generate the wide characters up to the current reading
507 position. */
508 int nread;
509 size_t wnread = (fp->_wide_data->_IO_read_ptr
510 - fp->_wide_data->_IO_read_base);
511 fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
512 nread = __libio_codecvt_length (cv, &fp->_wide_data->_IO_state,
513 fp->_IO_read_base,
514 fp->_IO_read_end, wnread);
515 fp->_IO_read_ptr = fp->_IO_read_base + nread;
516 delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
517 }
518
519 new_pos = _IO_SYSSEEK (fp, delta, 1);
520 if (new_pos != (off64_t) EOF)
521 {
522 fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
523 fp->_IO_read_end = fp->_IO_read_ptr;
524 }
525 else if (errno == ESPIPE)
526 ; /* Ignore error from unseekable devices. */
527 else
528 retval = WEOF;
529 }
530 if (retval != WEOF)
531 fp->_offset = _IO_pos_BAD;
532 /* FIXME: Cleanup - can this be shared? */
533 /* setg(base(), ptr, ptr); */
534 return retval;
535}
536libc_hidden_def (_IO_wfile_sync)
537
538/* Adjust the internal buffer pointers to reflect the state in the external
539 buffer. The content between fp->_IO_read_base and fp->_IO_read_ptr is
540 assumed to be converted and available in the range
541 fp->_wide_data->_IO_read_base and fp->_wide_data->_IO_read_end.
542
543 Returns 0 on success and -1 on error with the _IO_ERR_SEEN flag set. */
544static int
545adjust_wide_data (FILE *fp, bool do_convert)
546{
547 struct _IO_codecvt *cv = fp->_codecvt;
548
549 int clen = __libio_codecvt_encoding (cv);
550
551 /* Take the easy way out for constant length encodings if we don't need to
552 convert. */
553 if (!do_convert && clen > 0)
554 {
555 fp->_wide_data->_IO_read_end += ((fp->_IO_read_ptr - fp->_IO_read_base)
556 / clen);
557 goto done;
558 }
559
560 enum __codecvt_result status;
561 const char *read_stop = (const char *) fp->_IO_read_base;
562 do
563 {
564
565 fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
566 status = __libio_codecvt_in (cv, &fp->_wide_data->_IO_state,
567 fp->_IO_read_base, fp->_IO_read_ptr,
568 &read_stop,
569 fp->_wide_data->_IO_read_base,
570 fp->_wide_data->_IO_buf_end,
571 &fp->_wide_data->_IO_read_end);
572
573 /* Should we return EILSEQ? */
574 if (__glibc_unlikely (status == __codecvt_error))
575 {
576 fp->_flags |= _IO_ERR_SEEN;
577 return -1;
578 }
579 }
580 while (__builtin_expect (status == __codecvt_partial, 0));
581
582done:
583 /* Now seek to _IO_read_end to behave as if we have read it all in. */
584 fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
585
586 return 0;
587}
588
589/* ftell{,o} implementation for wide mode. Don't modify any state of the file
590 pointer while we try to get the current state of the stream except in one
591 case, which is when we have unflushed writes in append mode. */
592static off64_t
593do_ftell_wide (FILE *fp)
594{
595 off64_t result, offset = 0;
596
597 /* No point looking for offsets in the buffer if it hasn't even been
598 allocated. */
599 if (fp->_wide_data->_IO_buf_base != NULL)
600 {
601 const wchar_t *wide_read_base;
602 const wchar_t *wide_read_ptr;
603 const wchar_t *wide_read_end;
604 bool unflushed_writes = (fp->_wide_data->_IO_write_ptr
605 > fp->_wide_data->_IO_write_base);
606
607 bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING;
608
609 /* When we have unflushed writes in append mode, seek to the end of the
610 file and record that offset. This is the only time we change the file
611 stream state and it is safe since the file handle is active. */
612 if (unflushed_writes && append_mode)
613 {
614 result = _IO_SYSSEEK (fp, 0, _IO_seek_end);
615 if (result == _IO_pos_BAD)
616 return EOF;
617 else
618 fp->_offset = result;
619 }
620
621 /* XXX For wide stream with backup store it is not very
622 reasonable to determine the offset. The pushed-back
623 character might require a state change and we need not be
624 able to compute the initial state by reverse transformation
625 since there is no guarantee of symmetry. So we don't even
626 try and return an error. */
627 if (_IO_in_backup (fp))
628 {
629 if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
630 {
631 __set_errno (EINVAL);
632 return -1;
633 }
634
635 /* Nothing in the backup store, so note the backed up pointers
636 without changing the state. */
637 wide_read_base = fp->_wide_data->_IO_save_base;
638 wide_read_ptr = wide_read_base;
639 wide_read_end = fp->_wide_data->_IO_save_end;
640 }
641 else
642 {
643 wide_read_base = fp->_wide_data->_IO_read_base;
644 wide_read_ptr = fp->_wide_data->_IO_read_ptr;
645 wide_read_end = fp->_wide_data->_IO_read_end;
646 }
647
648 struct _IO_codecvt *cv = fp->_codecvt;
649 int clen = __libio_codecvt_encoding (cv);
650
651 if (!unflushed_writes)
652 {
653 if (clen > 0)
654 {
655 offset -= (wide_read_end - wide_read_ptr) * clen;
656 offset -= fp->_IO_read_end - fp->_IO_read_ptr;
657 }
658 else
659 {
660 int nread;
661
662 size_t delta = wide_read_ptr - wide_read_base;
663 __mbstate_t state = fp->_wide_data->_IO_last_state;
664 nread = __libio_codecvt_length (cv, &state,
665 fp->_IO_read_base,
666 fp->_IO_read_end, delta);
667 offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
668 }
669 }
670 else
671 {
672 if (clen > 0)
673 offset += (fp->_wide_data->_IO_write_ptr
674 - fp->_wide_data->_IO_write_base) * clen;
675 else
676 {
677 size_t delta = (fp->_wide_data->_IO_write_ptr
678 - fp->_wide_data->_IO_write_base);
679
680 /* Allocate enough space for the conversion. */
681 size_t outsize = delta * sizeof (wchar_t);
682 char *out = malloc (size: outsize);
683 char *outstop = out;
684 const wchar_t *in = fp->_wide_data->_IO_write_base;
685
686 enum __codecvt_result status;
687
688 __mbstate_t state = fp->_wide_data->_IO_last_state;
689 status = __libio_codecvt_out (cv, &state, in, in + delta, &in,
690 out, out + outsize, &outstop);
691
692 /* We don't check for __codecvt_partial because it can be
693 returned on one of two conditions: either the output
694 buffer is full or the input sequence is incomplete. We
695 take care to allocate enough buffer and our input
696 sequences must be complete since they are accepted as
697 wchar_t; if not, then that is an error. */
698 if (__glibc_unlikely (status != __codecvt_ok))
699 {
700 free (ptr: out);
701 return WEOF;
702 }
703
704 offset += outstop - out;
705 free (ptr: out);
706 }
707
708 /* We don't trust _IO_read_end to represent the current file offset
709 when writing in append mode because the value would have to be
710 shifted to the end of the file during a flush. Use the write base
711 instead, along with the new offset we got above when we did a seek
712 to the end of the file. */
713 if (append_mode)
714 offset += fp->_IO_write_ptr - fp->_IO_write_base;
715 /* For all other modes, _IO_read_end represents the file offset. */
716 else
717 offset += fp->_IO_write_ptr - fp->_IO_read_end;
718 }
719 }
720
721 if (fp->_offset != _IO_pos_BAD)
722 result = fp->_offset;
723 else
724 result = _IO_SYSSEEK (fp, 0, _IO_seek_cur);
725
726 if (result == EOF)
727 return result;
728
729 result += offset;
730
731 if (result < 0)
732 {
733 __set_errno (EINVAL);
734 return EOF;
735 }
736
737 return result;
738}
739
740off64_t
741_IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
742{
743 off64_t result;
744 off64_t delta, new_offset;
745 long int count;
746
747 /* Short-circuit into a separate function. We don't want to mix any
748 functionality and we don't want to touch anything inside the FILE
749 object. */
750 if (mode == 0)
751 return do_ftell_wide (fp);
752
753 /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
754 offset of the underlying file must be exact. */
755 int must_be_exact = ((fp->_wide_data->_IO_read_base
756 == fp->_wide_data->_IO_read_end)
757 && (fp->_wide_data->_IO_write_base
758 == fp->_wide_data->_IO_write_ptr));
759
760 bool was_writing = ((fp->_wide_data->_IO_write_ptr
761 > fp->_wide_data->_IO_write_base)
762 || _IO_in_put_mode (fp));
763
764 /* Flush unwritten characters.
765 (This may do an unneeded write if we seek within the buffer.
766 But to be able to switch to reading, we would need to set
767 egptr to pptr. That can't be done in the current design,
768 which assumes file_ptr() is eGptr. Anyway, since we probably
769 end up flushing when we close(), it doesn't make much difference.)
770 FIXME: simulate mem-mapped files. */
771 if (was_writing && _IO_switch_to_wget_mode (fp))
772 return WEOF;
773
774 if (fp->_wide_data->_IO_buf_base == NULL)
775 {
776 /* It could be that we already have a pushback buffer. */
777 if (fp->_wide_data->_IO_read_base != NULL)
778 {
779 free (ptr: fp->_wide_data->_IO_read_base);
780 fp->_flags &= ~_IO_IN_BACKUP;
781 }
782 _IO_doallocbuf (fp);
783 _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
784 _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
785 _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
786 fp->_wide_data->_IO_buf_base);
787 _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
788 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
789 }
790
791 switch (dir)
792 {
793 struct _IO_codecvt *cv;
794 int clen;
795
796 case _IO_seek_cur:
797 /* Adjust for read-ahead (bytes is buffer). To do this we must
798 find out which position in the external buffer corresponds to
799 the current position in the internal buffer. */
800 cv = fp->_codecvt;
801 clen = __libio_codecvt_encoding (cv);
802
803 if (mode != 0 || !was_writing)
804 {
805 if (clen > 0)
806 {
807 offset -= (fp->_wide_data->_IO_read_end
808 - fp->_wide_data->_IO_read_ptr) * clen;
809 /* Adjust by readahead in external buffer. */
810 offset -= fp->_IO_read_end - fp->_IO_read_ptr;
811 }
812 else
813 {
814 int nread;
815
816 delta = (fp->_wide_data->_IO_read_ptr
817 - fp->_wide_data->_IO_read_base);
818 fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
819 nread = __libio_codecvt_length (cv,
820 &fp->_wide_data->_IO_state,
821 fp->_IO_read_base,
822 fp->_IO_read_end, delta);
823 fp->_IO_read_ptr = fp->_IO_read_base + nread;
824 fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
825 offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
826 }
827 }
828
829 if (fp->_offset == _IO_pos_BAD)
830 goto dumb;
831
832 /* Make offset absolute, assuming current pointer is file_ptr(). */
833 offset += fp->_offset;
834
835 dir = _IO_seek_set;
836 break;
837 case _IO_seek_set:
838 break;
839 case _IO_seek_end:
840 {
841 struct __stat64_t64 st;
842 if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode))
843 {
844 offset += st.st_size;
845 dir = _IO_seek_set;
846 }
847 else
848 goto dumb;
849 }
850 }
851
852 _IO_free_wbackup_area (fp);
853
854 /* At this point, dir==_IO_seek_set. */
855
856 /* If destination is within current buffer, optimize: */
857 if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
858 && !_IO_in_backup (fp))
859 {
860 off64_t start_offset = (fp->_offset
861 - (fp->_IO_read_end - fp->_IO_buf_base));
862 if (offset >= start_offset && offset < fp->_offset)
863 {
864 _IO_setg (fp, fp->_IO_buf_base,
865 fp->_IO_buf_base + (offset - start_offset),
866 fp->_IO_read_end);
867 _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
868 _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
869 fp->_wide_data->_IO_buf_base,
870 fp->_wide_data->_IO_buf_base);
871 _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
872 fp->_wide_data->_IO_buf_base);
873
874 if (adjust_wide_data (fp, false))
875 goto dumb;
876
877 _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
878 goto resync;
879 }
880 }
881
882 if (fp->_flags & _IO_NO_READS)
883 goto dumb;
884
885 /* Try to seek to a block boundary, to improve kernel page management. */
886 new_offset = offset & ~(fp->_IO_buf_end - fp->_IO_buf_base - 1);
887 delta = offset - new_offset;
888 if (delta > fp->_IO_buf_end - fp->_IO_buf_base)
889 {
890 new_offset = offset;
891 delta = 0;
892 }
893 result = _IO_SYSSEEK (fp, new_offset, 0);
894 if (result < 0)
895 return EOF;
896 if (delta == 0)
897 count = 0;
898 else
899 {
900 count = _IO_SYSREAD (fp, fp->_IO_buf_base,
901 (must_be_exact
902 ? delta : fp->_IO_buf_end - fp->_IO_buf_base));
903 if (count < delta)
904 {
905 /* We weren't allowed to read, but try to seek the remainder. */
906 offset = count == EOF ? delta : delta-count;
907 dir = _IO_seek_cur;
908 goto dumb;
909 }
910 }
911 _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + delta,
912 fp->_IO_buf_base + count);
913 _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
914 _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
915 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
916 _IO_wsetp (fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
917
918 if (adjust_wide_data (fp, true))
919 goto dumb;
920
921 fp->_offset = result + count;
922 _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
923 return offset;
924 dumb:
925
926 _IO_unsave_markers (fp);
927 result = _IO_SYSSEEK (fp, offset, dir);
928 if (result != EOF)
929 {
930 _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
931 fp->_offset = result;
932 _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
933 _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
934 _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
935 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
936 _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
937 fp->_wide_data->_IO_buf_base);
938 }
939 return result;
940
941resync:
942 /* We need to do it since it is possible that the file offset in
943 the kernel may be changed behind our back. It may happen when
944 we fopen a file and then do a fork. One process may access the
945 file and the kernel file offset will be changed. */
946 if (fp->_offset >= 0)
947 _IO_SYSSEEK (fp, fp->_offset, 0);
948
949 return offset;
950}
951libc_hidden_def (_IO_wfile_seekoff)
952
953
954size_t
955_IO_wfile_xsputn (FILE *f, const void *data, size_t n)
956{
957 const wchar_t *s = (const wchar_t *) data;
958 size_t to_do = n;
959 int must_flush = 0;
960 size_t count;
961
962 if (n <= 0)
963 return 0;
964 /* This is an optimized implementation.
965 If the amount to be written straddles a block boundary
966 (or the filebuf is unbuffered), use sys_write directly. */
967
968 /* First figure out how much space is available in the buffer. */
969 count = f->_wide_data->_IO_write_end - f->_wide_data->_IO_write_ptr;
970 if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
971 {
972 count = f->_wide_data->_IO_buf_end - f->_wide_data->_IO_write_ptr;
973 if (count >= n)
974 {
975 const wchar_t *p;
976 for (p = s + n; p > s; )
977 {
978 if (*--p == L'\n')
979 {
980 count = p - s + 1;
981 must_flush = 1;
982 break;
983 }
984 }
985 }
986 }
987 /* Then fill the buffer. */
988 if (count > 0)
989 {
990 if (count > to_do)
991 count = to_do;
992 if (count > 20)
993 {
994 f->_wide_data->_IO_write_ptr =
995 __wmempcpy (s1: f->_wide_data->_IO_write_ptr, s2: s, n: count);
996 s += count;
997 }
998 else
999 {
1000 wchar_t *p = f->_wide_data->_IO_write_ptr;
1001 int i = (int) count;
1002 while (--i >= 0)
1003 *p++ = *s++;
1004 f->_wide_data->_IO_write_ptr = p;
1005 }
1006 to_do -= count;
1007 }
1008 if (to_do > 0)
1009 to_do -= _IO_wdefault_xsputn (f, s, to_do);
1010 if (must_flush
1011 && f->_wide_data->_IO_write_ptr != f->_wide_data->_IO_write_base)
1012 _IO_wdo_write (fp: f, data: f->_wide_data->_IO_write_base,
1013 to_do: f->_wide_data->_IO_write_ptr
1014 - f->_wide_data->_IO_write_base);
1015
1016 return n - to_do;
1017}
1018libc_hidden_def (_IO_wfile_xsputn)
1019
1020
1021const struct _IO_jump_t _IO_wfile_jumps libio_vtable =
1022{
1023 JUMP_INIT_DUMMY,
1024 JUMP_INIT(finish, _IO_new_file_finish),
1025 JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
1026 JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow),
1027 JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
1028 JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
1029 JUMP_INIT(xsputn, _IO_wfile_xsputn),
1030 JUMP_INIT(xsgetn, _IO_file_xsgetn),
1031 JUMP_INIT(seekoff, _IO_wfile_seekoff),
1032 JUMP_INIT(seekpos, _IO_default_seekpos),
1033 JUMP_INIT(setbuf, _IO_new_file_setbuf),
1034 JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
1035 JUMP_INIT(doallocate, _IO_wfile_doallocate),
1036 JUMP_INIT(read, _IO_file_read),
1037 JUMP_INIT(write, _IO_new_file_write),
1038 JUMP_INIT(seek, _IO_file_seek),
1039 JUMP_INIT(close, _IO_file_close),
1040 JUMP_INIT(stat, _IO_file_stat),
1041 JUMP_INIT(showmanyc, _IO_default_showmanyc),
1042 JUMP_INIT(imbue, _IO_default_imbue)
1043};
1044libc_hidden_data_def (_IO_wfile_jumps)
1045
1046
1047const struct _IO_jump_t _IO_wfile_jumps_mmap libio_vtable =
1048{
1049 JUMP_INIT_DUMMY,
1050 JUMP_INIT(finish, _IO_new_file_finish),
1051 JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
1052 JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_mmap),
1053 JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
1054 JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
1055 JUMP_INIT(xsputn, _IO_wfile_xsputn),
1056 JUMP_INIT(xsgetn, _IO_file_xsgetn),
1057 JUMP_INIT(seekoff, _IO_wfile_seekoff),
1058 JUMP_INIT(seekpos, _IO_default_seekpos),
1059 JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
1060 JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
1061 JUMP_INIT(doallocate, _IO_wfile_doallocate),
1062 JUMP_INIT(read, _IO_file_read),
1063 JUMP_INIT(write, _IO_new_file_write),
1064 JUMP_INIT(seek, _IO_file_seek),
1065 JUMP_INIT(close, _IO_file_close_mmap),
1066 JUMP_INIT(stat, _IO_file_stat),
1067 JUMP_INIT(showmanyc, _IO_default_showmanyc),
1068 JUMP_INIT(imbue, _IO_default_imbue)
1069};
1070
1071const struct _IO_jump_t _IO_wfile_jumps_maybe_mmap libio_vtable =
1072{
1073 JUMP_INIT_DUMMY,
1074 JUMP_INIT(finish, _IO_new_file_finish),
1075 JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
1076 JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow_maybe_mmap),
1077 JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
1078 JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
1079 JUMP_INIT(xsputn, _IO_wfile_xsputn),
1080 JUMP_INIT(xsgetn, _IO_file_xsgetn),
1081 JUMP_INIT(seekoff, _IO_wfile_seekoff),
1082 JUMP_INIT(seekpos, _IO_default_seekpos),
1083 JUMP_INIT(setbuf, _IO_file_setbuf_mmap),
1084 JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
1085 JUMP_INIT(doallocate, _IO_wfile_doallocate),
1086 JUMP_INIT(read, _IO_file_read),
1087 JUMP_INIT(write, _IO_new_file_write),
1088 JUMP_INIT(seek, _IO_file_seek),
1089 JUMP_INIT(close, _IO_file_close),
1090 JUMP_INIT(stat, _IO_file_stat),
1091 JUMP_INIT(showmanyc, _IO_default_showmanyc),
1092 JUMP_INIT(imbue, _IO_default_imbue)
1093};
1094

source code of glibc/libio/wfileops.c