9c9d30396826c0de091fe9467b979b128a13e25e
[platform/upstream/glibc.git] / libio / wstrops.c
1 /* Copyright (C) 1993-2023 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 "strfile.h"
29 #include "libioP.h"
30 #include <string.h>
31 #include <wchar.h>
32 #include <stdio_ext.h>
33
34 void
35 _IO_wstr_init_static (FILE *fp, wchar_t *ptr, size_t size,
36                       wchar_t *pstart)
37 {
38   wchar_t *end;
39
40   if (size == 0)
41     end = ptr + __wcslen (ptr);
42   else if ((size_t) ptr + size * sizeof (wchar_t) > (size_t) ptr)
43     end = ptr + size;
44   else
45     /* Even for misaligned ptr make sure there is integral number of wide
46        characters.  */
47     end = ptr + (-1 - (size_t) ptr) / sizeof (wchar_t);
48   _IO_wsetb (fp, ptr, end, 0);
49
50   fp->_wide_data->_IO_write_base = ptr;
51   fp->_wide_data->_IO_read_base = ptr;
52   fp->_wide_data->_IO_read_ptr = ptr;
53   if (pstart)
54     {
55       fp->_wide_data->_IO_write_ptr = pstart;
56       fp->_wide_data->_IO_write_end = end;
57       fp->_wide_data->_IO_read_end = pstart;
58     }
59   else
60     {
61       fp->_wide_data->_IO_write_ptr = ptr;
62       fp->_wide_data->_IO_write_end = ptr;
63       fp->_wide_data->_IO_read_end = end;
64     }
65   /* A null _allocate_buffer function flags the strfile as being static. */
66   (((_IO_strfile *) fp)->_s._allocate_buffer_unused) = (_IO_alloc_type)0;
67 }
68
69 wint_t
70 _IO_wstr_overflow (FILE *fp, wint_t c)
71 {
72   int flush_only = c == WEOF;
73   size_t pos;
74   if (fp->_flags & _IO_NO_WRITES)
75       return flush_only ? 0 : WEOF;
76   if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
77     {
78       fp->_flags |= _IO_CURRENTLY_PUTTING;
79       fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_read_ptr;
80       fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
81     }
82   pos = fp->_wide_data->_IO_write_ptr - fp->_wide_data->_IO_write_base;
83   if (pos >= (size_t) (_IO_wblen (fp) + flush_only))
84     {
85       if (fp->_flags2 & _IO_FLAGS2_USER_WBUF) /* not allowed to enlarge */
86         return WEOF;
87       else
88         {
89           wchar_t *new_buf;
90           wchar_t *old_buf = fp->_wide_data->_IO_buf_base;
91           size_t old_wblen = _IO_wblen (fp);
92           size_t new_size = 2 * old_wblen + 100;
93
94           if (__glibc_unlikely (new_size < old_wblen)
95               || __glibc_unlikely (new_size > SIZE_MAX / sizeof (wchar_t)))
96             return EOF;
97
98           new_buf = malloc (new_size * sizeof (wchar_t));
99           if (new_buf == NULL)
100             {
101               /*          __ferror(fp) = 1; */
102               return WEOF;
103             }
104           if (old_buf)
105             {
106               __wmemcpy (new_buf, old_buf, old_wblen);
107               free (old_buf);
108               /* Make sure _IO_setb won't try to delete _IO_buf_base. */
109               fp->_wide_data->_IO_buf_base = NULL;
110             }
111
112           __wmemset (new_buf + old_wblen, L'\0', new_size - old_wblen);
113
114           _IO_wsetb (fp, new_buf, new_buf + new_size, 1);
115           fp->_wide_data->_IO_read_base =
116             new_buf + (fp->_wide_data->_IO_read_base - old_buf);
117           fp->_wide_data->_IO_read_ptr =
118             new_buf + (fp->_wide_data->_IO_read_ptr - old_buf);
119           fp->_wide_data->_IO_read_end =
120             new_buf + (fp->_wide_data->_IO_read_end - old_buf);
121           fp->_wide_data->_IO_write_ptr =
122             new_buf + (fp->_wide_data->_IO_write_ptr - old_buf);
123
124           fp->_wide_data->_IO_write_base = new_buf;
125           fp->_wide_data->_IO_write_end = fp->_wide_data->_IO_buf_end;
126         }
127     }
128
129   if (!flush_only)
130     *fp->_wide_data->_IO_write_ptr++ = c;
131   if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)
132     fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
133   if (flush_only)
134     return 0;
135   else
136     return c;
137 }
138
139
140 wint_t
141 _IO_wstr_underflow (FILE *fp)
142 {
143   if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)
144     fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
145   if ((fp->_flags & _IO_TIED_PUT_GET) && (fp->_flags & _IO_CURRENTLY_PUTTING))
146     {
147       fp->_flags &= ~_IO_CURRENTLY_PUTTING;
148       fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_write_ptr;
149       fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_end;
150     }
151   if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
152     return *fp->_wide_data->_IO_read_ptr;
153   else
154     return WEOF;
155 }
156
157
158 /* The size of the valid part of the buffer.  */
159 ssize_t
160 _IO_wstr_count (FILE *fp)
161 {
162   struct _IO_wide_data *wd = fp->_wide_data;
163
164   return ((wd->_IO_write_ptr > wd->_IO_read_end
165            ? wd->_IO_write_ptr : wd->_IO_read_end)
166           - wd->_IO_read_base);
167 }
168
169
170 static int
171 enlarge_userbuf (FILE *fp, off64_t offset, int reading)
172 {
173   if ((ssize_t) offset <= _IO_wblen (fp))
174     return 0;
175
176   struct _IO_wide_data *wd = fp->_wide_data;
177
178   ssize_t oldend = wd->_IO_write_end - wd->_IO_write_base;
179
180   /* Try to enlarge the buffer.  */
181   if (fp->_flags2 & _IO_FLAGS2_USER_WBUF)
182     /* User-provided buffer.  */
183     return 1;
184
185   size_t newsize = offset + 100;
186   if (__glibc_unlikely (newsize > SIZE_MAX / sizeof (wchar_t)))
187     return 1;
188
189   wchar_t *oldbuf = wd->_IO_buf_base;
190   wchar_t *newbuf = malloc (newsize * sizeof (wchar_t));
191   if (newbuf == NULL)
192     return 1;
193
194   if (oldbuf != NULL)
195     {
196       __wmemcpy (newbuf, oldbuf, _IO_wblen (fp));
197       free (oldbuf);
198       /* Make sure _IO_setb won't try to delete
199          _IO_buf_base. */
200       wd->_IO_buf_base = NULL;
201     }
202
203   _IO_wsetb (fp, newbuf, newbuf + newsize, 1);
204
205   if (reading)
206     {
207       wd->_IO_write_base = newbuf + (wd->_IO_write_base - oldbuf);
208       wd->_IO_write_ptr = newbuf + (wd->_IO_write_ptr - oldbuf);
209       wd->_IO_write_end = newbuf + (wd->_IO_write_end - oldbuf);
210       wd->_IO_read_ptr = newbuf + (wd->_IO_read_ptr - oldbuf);
211
212       wd->_IO_read_base = newbuf;
213       wd->_IO_read_end = wd->_IO_buf_end;
214     }
215   else
216     {
217       wd->_IO_read_base = newbuf + (wd->_IO_read_base - oldbuf);
218       wd->_IO_read_ptr = newbuf + (wd->_IO_read_ptr - oldbuf);
219       wd->_IO_read_end = newbuf + (wd->_IO_read_end - oldbuf);
220       wd->_IO_write_ptr = newbuf + (wd->_IO_write_ptr - oldbuf);
221
222       wd->_IO_write_base = newbuf;
223       wd->_IO_write_end = wd->_IO_buf_end;
224     }
225
226   /* Clear the area between the last write position and th
227      new position.  */
228   assert (offset >= oldend);
229   if (reading)
230     __wmemset (wd->_IO_read_base + oldend, L'\0', offset - oldend);
231   else
232     __wmemset (wd->_IO_write_base + oldend, L'\0', offset - oldend);
233
234   return 0;
235 }
236
237 static void
238 _IO_wstr_switch_to_get_mode (FILE *fp)
239 {
240   if (_IO_in_backup (fp))
241     fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_backup_base;
242   else
243     {
244       fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_buf_base;
245       if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)
246         fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
247     }
248   fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_write_ptr;
249   fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
250
251   fp->_flags &= ~_IO_CURRENTLY_PUTTING;
252 }
253
254 off64_t
255 _IO_wstr_seekoff (FILE *fp, off64_t offset, int dir, int mode)
256 {
257   off64_t new_pos;
258
259   if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))
260     mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);
261
262   bool was_writing = ((fp->_wide_data->_IO_write_ptr
263                        > fp->_wide_data->_IO_write_base)
264                      || _IO_in_put_mode (fp));
265   if (was_writing)
266     _IO_wstr_switch_to_get_mode (fp);
267
268   if (mode == 0)
269     {
270       new_pos = (fp->_wide_data->_IO_write_ptr
271                  - fp->_wide_data->_IO_write_base);
272     }
273   else
274     {
275       ssize_t cur_size = _IO_wstr_count (fp);
276       new_pos = EOF;
277
278       /* Move the get pointer, if requested. */
279       if (mode & _IOS_INPUT)
280         {
281           ssize_t base;
282           switch (dir)
283             {
284             case _IO_seek_set:
285               base = 0;
286               break;
287             case _IO_seek_cur:
288               base = (fp->_wide_data->_IO_read_ptr
289                      - fp->_wide_data->_IO_read_base);
290               break;
291             default: /* case _IO_seek_end: */
292               base = cur_size;
293               break;
294             }
295           ssize_t maxval = SSIZE_MAX/sizeof (wchar_t) - base;
296           if (offset < -base || offset > maxval)
297             {
298               __set_errno (EINVAL);
299               return EOF;
300             }
301           base += offset;
302           if (base > cur_size
303               && enlarge_userbuf (fp, base, 1) != 0)
304             return EOF;
305           fp->_wide_data->_IO_read_ptr = (fp->_wide_data->_IO_read_base
306                                           + base);
307           fp->_wide_data->_IO_read_end = (fp->_wide_data->_IO_read_base
308                                           + cur_size);
309           new_pos = offset;
310         }
311
312       /* Move the put pointer, if requested. */
313       if (mode & _IOS_OUTPUT)
314         {
315           ssize_t base;
316           switch (dir)
317             {
318             case _IO_seek_set:
319               base = 0;
320               break;
321             case _IO_seek_cur:
322               base = (fp->_wide_data->_IO_write_ptr
323                      - fp->_wide_data->_IO_write_base);
324               break;
325             default: /* case _IO_seek_end: */
326               base = cur_size;
327               break;
328             }
329           ssize_t maxval = SSIZE_MAX/sizeof (wchar_t) - base;
330           if (offset < -base || offset > maxval)
331             {
332               __set_errno (EINVAL);
333               return EOF;
334             }
335           base += offset;
336           if (base > cur_size
337               && enlarge_userbuf (fp, base, 0) != 0)
338             return EOF;
339           fp->_wide_data->_IO_write_ptr = (fp->_wide_data->_IO_write_base
340                                            + base);
341           new_pos = base;
342         }
343     }
344   return new_pos;
345 }
346
347 wint_t
348 _IO_wstr_pbackfail (FILE *fp, wint_t c)
349 {
350   if ((fp->_flags & _IO_NO_WRITES) && c != WEOF)
351     return WEOF;
352   return _IO_wdefault_pbackfail (fp, c);
353 }
354
355 void
356 _IO_wstr_finish (FILE *fp, int dummy)
357 {
358   if (fp->_wide_data->_IO_buf_base && !(fp->_flags2 & _IO_FLAGS2_USER_WBUF))
359     free (fp->_wide_data->_IO_buf_base);
360   fp->_wide_data->_IO_buf_base = NULL;
361
362   _IO_wdefault_finish (fp, 0);
363 }
364
365 const struct _IO_jump_t _IO_wstr_jumps libio_vtable =
366 {
367   JUMP_INIT_DUMMY,
368   JUMP_INIT(finish, _IO_wstr_finish),
369   JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstr_overflow),
370   JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
371   JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
372   JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
373   JUMP_INIT(xsputn, _IO_wdefault_xsputn),
374   JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
375   JUMP_INIT(seekoff, _IO_wstr_seekoff),
376   JUMP_INIT(seekpos, _IO_default_seekpos),
377   JUMP_INIT(setbuf, _IO_default_setbuf),
378   JUMP_INIT(sync, _IO_default_sync),
379   JUMP_INIT(doallocate, _IO_wdefault_doallocate),
380   JUMP_INIT(read, _IO_default_read),
381   JUMP_INIT(write, _IO_default_write),
382   JUMP_INIT(seek, _IO_default_seek),
383   JUMP_INIT(close, _IO_default_close),
384   JUMP_INIT(stat, _IO_default_stat),
385   JUMP_INIT(showmanyc, _IO_default_showmanyc),
386   JUMP_INIT(imbue, _IO_default_imbue)
387 };