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