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/* Generic or default I/O operations. */
28
29#include "libioP.h"
30#include <stdlib.h>
31#include <string.h>
32#include <wchar.h>
33
34
35static int save_for_wbackup (FILE *fp, wchar_t *end_p) __THROW;
36
37/* Return minimum _pos markers
38 Assumes the current get area is the main get area. */
39ssize_t
40_IO_least_wmarker (FILE *fp, wchar_t *end_p)
41{
42 ssize_t least_so_far = end_p - fp->_wide_data->_IO_read_base;
43 struct _IO_marker *mark;
44 for (mark = fp->_markers; mark != NULL; mark = mark->_next)
45 if (mark->_pos < least_so_far)
46 least_so_far = mark->_pos;
47 return least_so_far;
48}
49libc_hidden_def (_IO_least_wmarker)
50
51/* Switch current get area from backup buffer to (start of) main get area. */
52void
53_IO_switch_to_main_wget_area (FILE *fp)
54{
55 wchar_t *tmp;
56 fp->_flags &= ~_IO_IN_BACKUP;
57 /* Swap _IO_read_end and _IO_save_end. */
58 tmp = fp->_wide_data->_IO_read_end;
59 fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_save_end;
60 fp->_wide_data->_IO_save_end= tmp;
61 /* Swap _IO_read_base and _IO_save_base. */
62 tmp = fp->_wide_data->_IO_read_base;
63 fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_save_base;
64 fp->_wide_data->_IO_save_base = tmp;
65 /* Set _IO_read_ptr. */
66 fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_base;
67}
68libc_hidden_def (_IO_switch_to_main_wget_area)
69
70
71/* Switch current get area from main get area to (end of) backup area. */
72void
73_IO_switch_to_wbackup_area (FILE *fp)
74{
75 wchar_t *tmp;
76 fp->_flags |= _IO_IN_BACKUP;
77 /* Swap _IO_read_end and _IO_save_end. */
78 tmp = fp->_wide_data->_IO_read_end;
79 fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_save_end;
80 fp->_wide_data->_IO_save_end = tmp;
81 /* Swap _IO_read_base and _IO_save_base. */
82 tmp = fp->_wide_data->_IO_read_base;
83 fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_save_base;
84 fp->_wide_data->_IO_save_base = tmp;
85 /* Set _IO_read_ptr. */
86 fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
87}
88libc_hidden_def (_IO_switch_to_wbackup_area)
89
90
91void
92_IO_wsetb (FILE *f, wchar_t *b, wchar_t *eb, int a)
93{
94 if (f->_wide_data->_IO_buf_base && !(f->_flags2 & _IO_FLAGS2_USER_WBUF))
95 free (ptr: f->_wide_data->_IO_buf_base);
96 f->_wide_data->_IO_buf_base = b;
97 f->_wide_data->_IO_buf_end = eb;
98 if (a)
99 f->_flags2 &= ~_IO_FLAGS2_USER_WBUF;
100 else
101 f->_flags2 |= _IO_FLAGS2_USER_WBUF;
102}
103libc_hidden_def (_IO_wsetb)
104
105
106wint_t
107_IO_wdefault_pbackfail (FILE *fp, wint_t c)
108{
109 if (fp->_wide_data->_IO_read_ptr > fp->_wide_data->_IO_read_base
110 && !_IO_in_backup (fp)
111 && (wint_t) fp->_IO_read_ptr[-1] == c)
112 --fp->_IO_read_ptr;
113 else
114 {
115 /* Need to handle a filebuf in write mode (switch to read mode). FIXME!*/
116 if (!_IO_in_backup (fp))
117 {
118 /* We need to keep the invariant that the main get area
119 logically follows the backup area. */
120 if (fp->_wide_data->_IO_read_ptr > fp->_wide_data->_IO_read_base
121 && _IO_have_wbackup (fp))
122 {
123 if (save_for_wbackup (fp, end_p: fp->_wide_data->_IO_read_ptr))
124 return WEOF;
125 }
126 else if (!_IO_have_wbackup (fp))
127 {
128 /* No backup buffer: allocate one. */
129 /* Use nshort buffer, if unused? (probably not) FIXME */
130 int backup_size = 128;
131 wchar_t *bbuf = (wchar_t *) malloc (size: backup_size
132 * sizeof (wchar_t));
133 if (bbuf == NULL)
134 return WEOF;
135 fp->_wide_data->_IO_save_base = bbuf;
136 fp->_wide_data->_IO_save_end = (fp->_wide_data->_IO_save_base
137 + backup_size);
138 fp->_wide_data->_IO_backup_base = fp->_wide_data->_IO_save_end;
139 }
140 fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr;
141 _IO_switch_to_wbackup_area (fp);
142 }
143 else if (fp->_wide_data->_IO_read_ptr <= fp->_wide_data->_IO_read_base)
144 {
145 /* Increase size of existing backup buffer. */
146 size_t new_size;
147 size_t old_size = (fp->_wide_data->_IO_read_end
148 - fp->_wide_data->_IO_read_base);
149 wchar_t *new_buf;
150 new_size = 2 * old_size;
151 new_buf = (wchar_t *) malloc (size: new_size * sizeof (wchar_t));
152 if (new_buf == NULL)
153 return WEOF;
154 __wmemcpy (s1: new_buf + (new_size - old_size),
155 s2: fp->_wide_data->_IO_read_base, n: old_size);
156 free (ptr: fp->_wide_data->_IO_read_base);
157 _IO_wsetg (fp, new_buf, new_buf + (new_size - old_size),
158 new_buf + new_size);
159 fp->_wide_data->_IO_backup_base = fp->_wide_data->_IO_read_ptr;
160 }
161
162 *--fp->_wide_data->_IO_read_ptr = c;
163 }
164 return c;
165}
166libc_hidden_def (_IO_wdefault_pbackfail)
167
168
169void
170_IO_wdefault_finish (FILE *fp, int dummy)
171{
172 struct _IO_marker *mark;
173 if (fp->_wide_data->_IO_buf_base && !(fp->_flags2 & _IO_FLAGS2_USER_WBUF))
174 {
175 free (ptr: fp->_wide_data->_IO_buf_base);
176 fp->_wide_data->_IO_buf_base = fp->_wide_data->_IO_buf_end = NULL;
177 }
178
179 for (mark = fp->_markers; mark != NULL; mark = mark->_next)
180 mark->_sbuf = NULL;
181
182 if (fp->_IO_save_base)
183 {
184 free (ptr: fp->_wide_data->_IO_save_base);
185 fp->_IO_save_base = NULL;
186 }
187
188#ifdef _IO_MTSAFE_IO
189 if (fp->_lock != NULL)
190 _IO_lock_fini (*fp->_lock);
191#endif
192
193 _IO_un_link ((struct _IO_FILE_plus *) fp);
194}
195libc_hidden_def (_IO_wdefault_finish)
196
197
198wint_t
199_IO_wdefault_uflow (FILE *fp)
200{
201 wint_t wch;
202 wch = _IO_UNDERFLOW (fp);
203 if (wch == WEOF)
204 return WEOF;
205 return *fp->_wide_data->_IO_read_ptr++;
206}
207libc_hidden_def (_IO_wdefault_uflow)
208
209
210wint_t
211__woverflow (FILE *f, wint_t wch)
212{
213 if (f->_mode == 0)
214 _IO_fwide (f, 1);
215 return _IO_OVERFLOW (f, wch);
216}
217libc_hidden_def (__woverflow)
218
219
220wint_t
221__wuflow (FILE *fp)
222{
223 if (fp->_mode < 0 || (fp->_mode == 0 && _IO_fwide (fp, 1) != 1))
224 return WEOF;
225
226 if (fp->_mode == 0)
227 _IO_fwide (fp, 1);
228 if (_IO_in_put_mode (fp))
229 if (_IO_switch_to_wget_mode (fp) == EOF)
230 return WEOF;
231 if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
232 return *fp->_wide_data->_IO_read_ptr++;
233 if (_IO_in_backup (fp))
234 {
235 _IO_switch_to_main_wget_area (fp);
236 if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
237 return *fp->_wide_data->_IO_read_ptr++;
238 }
239 if (_IO_have_markers (fp))
240 {
241 if (save_for_wbackup (fp, end_p: fp->_wide_data->_IO_read_end))
242 return WEOF;
243 }
244 else if (_IO_have_wbackup (fp))
245 _IO_free_wbackup_area (fp);
246 return _IO_UFLOW (fp);
247}
248libc_hidden_def (__wuflow)
249
250wint_t
251__wunderflow (FILE *fp)
252{
253 if (fp->_mode < 0 || (fp->_mode == 0 && _IO_fwide (fp, 1) != 1))
254 return WEOF;
255
256 if (fp->_mode == 0)
257 _IO_fwide (fp, 1);
258 if (_IO_in_put_mode (fp))
259 if (_IO_switch_to_wget_mode (fp) == EOF)
260 return WEOF;
261 if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
262 return *fp->_wide_data->_IO_read_ptr;
263 if (_IO_in_backup (fp))
264 {
265 _IO_switch_to_main_wget_area (fp);
266 if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
267 return *fp->_wide_data->_IO_read_ptr;
268 }
269 if (_IO_have_markers (fp))
270 {
271 if (save_for_wbackup (fp, end_p: fp->_wide_data->_IO_read_end))
272 return WEOF;
273 }
274 else if (_IO_have_backup (fp))
275 _IO_free_wbackup_area (fp);
276 return _IO_UNDERFLOW (fp);
277}
278libc_hidden_def (__wunderflow)
279
280
281size_t
282_IO_wdefault_xsputn (FILE *f, const void *data, size_t n)
283{
284 const wchar_t *s = (const wchar_t *) data;
285 size_t more = n;
286 if (more <= 0)
287 return 0;
288 for (;;)
289 {
290 /* Space available. */
291 ssize_t count = (f->_wide_data->_IO_write_end
292 - f->_wide_data->_IO_write_ptr);
293 if (count > 0)
294 {
295 if ((size_t) count > more)
296 count = more;
297 if (count > 20)
298 {
299 f->_wide_data->_IO_write_ptr =
300 __wmempcpy (s1: f->_wide_data->_IO_write_ptr, s2: s, n: count);
301 s += count;
302 }
303 else if (count <= 0)
304 count = 0;
305 else
306 {
307 wchar_t *p = f->_wide_data->_IO_write_ptr;
308 ssize_t i;
309 for (i = count; --i >= 0; )
310 *p++ = *s++;
311 f->_wide_data->_IO_write_ptr = p;
312 }
313 more -= count;
314 }
315 if (more == 0 || __woverflow (f, wch: *s++) == WEOF)
316 break;
317 more--;
318 }
319 return n - more;
320}
321libc_hidden_def (_IO_wdefault_xsputn)
322
323
324size_t
325_IO_wdefault_xsgetn (FILE *fp, void *data, size_t n)
326{
327 size_t more = n;
328 wchar_t *s = (wchar_t*) data;
329 for (;;)
330 {
331 /* Data available. */
332 ssize_t count = (fp->_wide_data->_IO_read_end
333 - fp->_wide_data->_IO_read_ptr);
334 if (count > 0)
335 {
336 if ((size_t) count > more)
337 count = more;
338 if (count > 20)
339 {
340 s = __wmempcpy (s1: s, s2: fp->_wide_data->_IO_read_ptr, n: count);
341 fp->_wide_data->_IO_read_ptr += count;
342 }
343 else if (count <= 0)
344 count = 0;
345 else
346 {
347 wchar_t *p = fp->_wide_data->_IO_read_ptr;
348 int i = (int) count;
349 while (--i >= 0)
350 *s++ = *p++;
351 fp->_wide_data->_IO_read_ptr = p;
352 }
353 more -= count;
354 }
355 if (more == 0 || __wunderflow (fp) == WEOF)
356 break;
357 }
358 return n - more;
359}
360libc_hidden_def (_IO_wdefault_xsgetn)
361
362
363void
364_IO_wdoallocbuf (FILE *fp)
365{
366 if (fp->_wide_data->_IO_buf_base)
367 return;
368 if (!(fp->_flags & _IO_UNBUFFERED))
369 if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)
370 return;
371 _IO_wsetb (f: fp, b: fp->_wide_data->_shortbuf,
372 eb: fp->_wide_data->_shortbuf + 1, a: 0);
373}
374libc_hidden_def (_IO_wdoallocbuf)
375
376
377int
378_IO_wdefault_doallocate (FILE *fp)
379{
380 wchar_t *buf = (wchar_t *)malloc (BUFSIZ);
381 if (__glibc_unlikely (buf == NULL))
382 return EOF;
383
384 _IO_wsetb (f: fp, b: buf, eb: buf + BUFSIZ / sizeof *buf, a: 1);
385 return 1;
386}
387libc_hidden_def (_IO_wdefault_doallocate)
388
389
390int
391_IO_switch_to_wget_mode (FILE *fp)
392{
393 if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
394 if ((wint_t)_IO_WOVERFLOW (fp, WEOF) == WEOF)
395 return EOF;
396 if (_IO_in_backup (fp))
397 fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_backup_base;
398 else
399 {
400 fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_buf_base;
401 if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)
402 fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
403 }
404 fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_write_ptr;
405
406 fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr
407 = fp->_wide_data->_IO_write_end = fp->_wide_data->_IO_read_ptr;
408
409 fp->_flags &= ~_IO_CURRENTLY_PUTTING;
410 return 0;
411}
412libc_hidden_def (_IO_switch_to_wget_mode)
413
414void
415_IO_free_wbackup_area (FILE *fp)
416{
417 if (_IO_in_backup (fp))
418 _IO_switch_to_main_wget_area (fp); /* Just in case. */
419 free (ptr: fp->_wide_data->_IO_save_base);
420 fp->_wide_data->_IO_save_base = NULL;
421 fp->_wide_data->_IO_save_end = NULL;
422 fp->_wide_data->_IO_backup_base = NULL;
423}
424libc_hidden_def (_IO_free_wbackup_area)
425
426static int
427save_for_wbackup (FILE *fp, wchar_t *end_p)
428{
429 /* Append [_IO_read_base..end_p] to backup area. */
430 ssize_t least_mark = _IO_least_wmarker (fp, end_p);
431 /* needed_size is how much space we need in the backup area. */
432 size_t needed_size = ((end_p - fp->_wide_data->_IO_read_base)
433 - least_mark);
434 /* FIXME: Dubious arithmetic if pointers are NULL */
435 size_t current_Bsize = (fp->_wide_data->_IO_save_end
436 - fp->_wide_data->_IO_save_base);
437 size_t avail; /* Extra space available for future expansion. */
438 ssize_t delta;
439 struct _IO_marker *mark;
440 if (needed_size > current_Bsize)
441 {
442 wchar_t *new_buffer;
443 avail = 100;
444 new_buffer = (wchar_t *) malloc (size: (avail + needed_size)
445 * sizeof (wchar_t));
446 if (new_buffer == NULL)
447 return EOF; /* FIXME */
448 if (least_mark < 0)
449 {
450 __wmempcpy (s1: __wmempcpy (s1: new_buffer + avail,
451 s2: fp->_wide_data->_IO_save_end + least_mark,
452 n: -least_mark),
453 s2: fp->_wide_data->_IO_read_base,
454 n: end_p - fp->_wide_data->_IO_read_base);
455 }
456 else
457 {
458 __wmemcpy (s1: new_buffer + avail,
459 s2: fp->_wide_data->_IO_read_base + least_mark,
460 n: needed_size);
461 }
462 free (ptr: fp->_wide_data->_IO_save_base);
463 fp->_wide_data->_IO_save_base = new_buffer;
464 fp->_wide_data->_IO_save_end = new_buffer + avail + needed_size;
465 }
466 else
467 {
468 avail = current_Bsize - needed_size;
469 if (least_mark < 0)
470 {
471 __wmemmove (s1: fp->_wide_data->_IO_save_base + avail,
472 s2: fp->_wide_data->_IO_save_end + least_mark,
473 n: -least_mark);
474 __wmemcpy (s1: fp->_wide_data->_IO_save_base + avail - least_mark,
475 s2: fp->_wide_data->_IO_read_base,
476 n: end_p - fp->_wide_data->_IO_read_base);
477 }
478 else if (needed_size > 0)
479 __wmemcpy (s1: fp->_wide_data->_IO_save_base + avail,
480 s2: fp->_wide_data->_IO_read_base + least_mark,
481 n: needed_size);
482 }
483 fp->_wide_data->_IO_backup_base = fp->_wide_data->_IO_save_base + avail;
484 /* Adjust all the streammarkers. */
485 delta = end_p - fp->_wide_data->_IO_read_base;
486 for (mark = fp->_markers; mark != NULL; mark = mark->_next)
487 mark->_pos -= delta;
488 return 0;
489}
490
491wint_t
492_IO_sputbackwc (FILE *fp, wint_t c)
493{
494 wint_t result;
495
496 if (fp->_wide_data->_IO_read_ptr > fp->_wide_data->_IO_read_base
497 && (wchar_t)fp->_wide_data->_IO_read_ptr[-1] == (wchar_t) c)
498 {
499 fp->_wide_data->_IO_read_ptr--;
500 result = c;
501 }
502 else
503 result = _IO_PBACKFAIL (fp, c);
504
505 if (result != WEOF)
506 fp->_flags &= ~_IO_EOF_SEEN;
507
508 return result;
509}
510libc_hidden_def (_IO_sputbackwc)
511
512wint_t
513_IO_sungetwc (FILE *fp)
514{
515 wint_t result;
516
517 if (fp->_wide_data->_IO_read_ptr > fp->_wide_data->_IO_read_base)
518 {
519 fp->_wide_data->_IO_read_ptr--;
520 result = *fp->_wide_data->_IO_read_ptr;
521 }
522 else
523 result = _IO_PBACKFAIL (fp, EOF);
524
525 if (result != WEOF)
526 fp->_flags &= ~_IO_EOF_SEEN;
527
528 return result;
529}
530
531
532unsigned
533_IO_adjust_wcolumn (unsigned start, const wchar_t *line, int count)
534{
535 const wchar_t *ptr = line + count;
536 while (ptr > line)
537 if (*--ptr == L'\n')
538 return line + count - ptr - 1;
539 return start + count;
540}
541
542void
543_IO_init_wmarker (struct _IO_marker *marker, FILE *fp)
544{
545 marker->_sbuf = fp;
546 if (_IO_in_put_mode (fp))
547 _IO_switch_to_wget_mode (fp);
548 if (_IO_in_backup (fp))
549 marker->_pos = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_end;
550 else
551 marker->_pos = (fp->_wide_data->_IO_read_ptr
552 - fp->_wide_data->_IO_read_base);
553
554 /* Should perhaps sort the chain? */
555 marker->_next = fp->_markers;
556 fp->_markers = marker;
557}
558
559#define BAD_DELTA EOF
560
561/* Return difference between MARK and current position of MARK's stream. */
562int
563_IO_wmarker_delta (struct _IO_marker *mark)
564{
565 int cur_pos;
566 if (mark->_sbuf == NULL)
567 return BAD_DELTA;
568 if (_IO_in_backup (mark->_sbuf))
569 cur_pos = (mark->_sbuf->_wide_data->_IO_read_ptr
570 - mark->_sbuf->_wide_data->_IO_read_end);
571 else
572 cur_pos = (mark->_sbuf->_wide_data->_IO_read_ptr
573 - mark->_sbuf->_wide_data->_IO_read_base);
574 return mark->_pos - cur_pos;
575}
576
577int
578_IO_seekwmark (FILE *fp, struct _IO_marker *mark, int delta)
579{
580 if (mark->_sbuf != fp)
581 return EOF;
582 if (mark->_pos >= 0)
583 {
584 if (_IO_in_backup (fp))
585 _IO_switch_to_main_wget_area (fp);
586 fp->_wide_data->_IO_read_ptr = (fp->_wide_data->_IO_read_base
587 + mark->_pos);
588 }
589 else
590 {
591 if (!_IO_in_backup (fp))
592 _IO_switch_to_wbackup_area (fp);
593 fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end + mark->_pos;
594 }
595 return 0;
596}
597
598void
599_IO_unsave_wmarkers (FILE *fp)
600{
601 struct _IO_marker *mark = fp->_markers;
602 if (mark)
603 {
604 fp->_markers = 0;
605 }
606
607 if (_IO_have_backup (fp))
608 _IO_free_wbackup_area (fp);
609}
610

source code of glibc/libio/wgenops.c