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 "strfile.h"
29 #include "libioP.h"
30 #include <string.h>
31 #include <wchar.h>
32 #include <stdio_ext.h>
33
34 void
_IO_wstr_init_static(FILE * fp,wchar_t * ptr,size_t size,wchar_t * pstart)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
_IO_wstr_overflow(FILE * fp,wint_t c)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
_IO_wstr_underflow(FILE * fp)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
_IO_wstr_count(FILE * fp)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
enlarge_userbuf(FILE * fp,off64_t offset,int reading)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
_IO_wstr_switch_to_get_mode(FILE * fp)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
_IO_wstr_seekoff(FILE * fp,off64_t offset,int dir,int mode)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
_IO_wstr_pbackfail(FILE * fp,wint_t c)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
_IO_wstr_finish(FILE * fp,int dummy)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 };
388