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