1/* Copyright (C) 1993-2024 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 = strchr (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: 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, ptr: (char *) ptr, size: 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 (size: 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 (ptr: 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 if (flush_only)
137 return 0;
138 else
139 return c;
140}
141libc_hidden_def (_IO_str_overflow)
142
143int
144_IO_str_underflow (FILE *fp)
145{
146 if (fp->_IO_write_ptr > fp->_IO_read_end)
147 fp->_IO_read_end = fp->_IO_write_ptr;
148 if ((fp->_flags & _IO_TIED_PUT_GET) && (fp->_flags & _IO_CURRENTLY_PUTTING))
149 {
150 fp->_flags &= ~_IO_CURRENTLY_PUTTING;
151 fp->_IO_read_ptr = fp->_IO_write_ptr;
152 fp->_IO_write_ptr = fp->_IO_write_end;
153 }
154 if (fp->_IO_read_ptr < fp->_IO_read_end)
155 return *((unsigned char *) fp->_IO_read_ptr);
156 else
157 return EOF;
158}
159libc_hidden_def (_IO_str_underflow)
160
161/* The size of the valid part of the buffer. */
162
163ssize_t
164_IO_str_count (FILE *fp)
165{
166 return ((fp->_IO_write_ptr > fp->_IO_read_end
167 ? fp->_IO_write_ptr : fp->_IO_read_end)
168 - fp->_IO_read_base);
169}
170
171
172static int
173enlarge_userbuf (FILE *fp, off64_t offset, int reading)
174{
175 if ((ssize_t) offset <= _IO_blen (fp))
176 return 0;
177
178 ssize_t oldend = fp->_IO_write_end - fp->_IO_write_base;
179
180 /* Try to enlarge the buffer. */
181 if (fp->_flags & _IO_USER_BUF)
182 /* User-provided buffer. */
183 return 1;
184
185 size_t newsize = offset + 100;
186 char *oldbuf = fp->_IO_buf_base;
187 char *newbuf = malloc (size: newsize);
188 if (newbuf == NULL)
189 return 1;
190
191 if (oldbuf != NULL)
192 {
193 memcpy (newbuf, oldbuf, _IO_blen (fp));
194 free (ptr: oldbuf);
195 /* Make sure _IO_setb won't try to delete
196 _IO_buf_base. */
197 fp->_IO_buf_base = NULL;
198 }
199
200 _IO_setb (fp, newbuf, newbuf + newsize, 1);
201
202 if (reading)
203 {
204 fp->_IO_write_base = newbuf + (fp->_IO_write_base - oldbuf);
205 fp->_IO_write_ptr = newbuf + (fp->_IO_write_ptr - oldbuf);
206 fp->_IO_write_end = newbuf + (fp->_IO_write_end - oldbuf);
207 fp->_IO_read_ptr = newbuf + (fp->_IO_read_ptr - oldbuf);
208
209 fp->_IO_read_base = newbuf;
210 fp->_IO_read_end = fp->_IO_buf_end;
211 }
212 else
213 {
214 fp->_IO_read_base = newbuf + (fp->_IO_read_base - oldbuf);
215 fp->_IO_read_ptr = newbuf + (fp->_IO_read_ptr - oldbuf);
216 fp->_IO_read_end = newbuf + (fp->_IO_read_end - oldbuf);
217 fp->_IO_write_ptr = newbuf + (fp->_IO_write_ptr - oldbuf);
218
219 fp->_IO_write_base = newbuf;
220 fp->_IO_write_end = fp->_IO_buf_end;
221 }
222
223 /* Clear the area between the last write position and th
224 new position. */
225 assert (offset >= oldend);
226 if (reading)
227 memset (fp->_IO_read_base + oldend, '\0', offset - oldend);
228 else
229 memset (fp->_IO_write_base + oldend, '\0', offset - oldend);
230
231 return 0;
232}
233
234static void
235_IO_str_switch_to_get_mode (FILE *fp)
236{
237 if (_IO_in_backup (fp))
238 fp->_IO_read_base = fp->_IO_backup_base;
239 else
240 {
241 fp->_IO_read_base = fp->_IO_buf_base;
242 if (fp->_IO_write_ptr > fp->_IO_read_end)
243 fp->_IO_read_end = fp->_IO_write_ptr;
244 }
245 fp->_IO_read_ptr = fp->_IO_read_end = fp->_IO_write_ptr;
246
247 fp->_flags &= ~_IO_CURRENTLY_PUTTING;
248}
249
250off64_t
251_IO_str_seekoff (FILE *fp, off64_t offset, int dir, int mode)
252{
253 off64_t new_pos;
254
255 if (mode == 0 && (fp->_flags & _IO_TIED_PUT_GET))
256 mode = (fp->_flags & _IO_CURRENTLY_PUTTING ? _IOS_OUTPUT : _IOS_INPUT);
257
258 bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
259 || _IO_in_put_mode (fp));
260 if (was_writing)
261 _IO_str_switch_to_get_mode (fp);
262
263 if (mode == 0)
264 {
265 new_pos = fp->_IO_read_ptr - fp->_IO_read_base;
266 }
267 else
268 {
269 ssize_t cur_size = _IO_str_count(fp);
270 new_pos = EOF;
271
272 /* Move the get pointer, if requested. */
273 if (mode & _IOS_INPUT)
274 {
275 ssize_t base;
276 switch (dir)
277 {
278 case _IO_seek_set:
279 base = 0;
280 break;
281 case _IO_seek_cur:
282 base = fp->_IO_read_ptr - fp->_IO_read_base;
283 break;
284 default: /* case _IO_seek_end: */
285 base = cur_size;
286 break;
287 }
288 ssize_t maxval = SSIZE_MAX - base;
289 if (offset < -base || offset > maxval)
290 {
291 __set_errno (EINVAL);
292 return EOF;
293 }
294 base += offset;
295 if (base > cur_size
296 && enlarge_userbuf (fp, offset: base, reading: 1) != 0)
297 return EOF;
298 fp->_IO_read_ptr = fp->_IO_read_base + base;
299 fp->_IO_read_end = fp->_IO_read_base + cur_size;
300 new_pos = base;
301 }
302
303 /* Move the put pointer, if requested. */
304 if (mode & _IOS_OUTPUT)
305 {
306 ssize_t base;
307 switch (dir)
308 {
309 case _IO_seek_set:
310 base = 0;
311 break;
312 case _IO_seek_cur:
313 base = fp->_IO_write_ptr - fp->_IO_write_base;
314 break;
315 default: /* case _IO_seek_end: */
316 base = cur_size;
317 break;
318 }
319 ssize_t maxval = SSIZE_MAX - base;
320 if (offset < -base || offset > maxval)
321 {
322 __set_errno (EINVAL);
323 return EOF;
324 }
325 base += offset;
326 if (base > cur_size
327 && enlarge_userbuf (fp, offset: base, reading: 0) != 0)
328 return EOF;
329 fp->_IO_write_ptr = fp->_IO_write_base + base;
330 new_pos = base;
331 }
332 }
333 return new_pos;
334}
335libc_hidden_def (_IO_str_seekoff)
336
337int
338_IO_str_pbackfail (FILE *fp, int c)
339{
340 if ((fp->_flags & _IO_NO_WRITES) && c != EOF)
341 return EOF;
342 return _IO_default_pbackfail (fp, c);
343}
344libc_hidden_def (_IO_str_pbackfail)
345
346void
347_IO_str_finish (FILE *fp, int dummy)
348{
349 if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
350 free (ptr: fp->_IO_buf_base);
351 fp->_IO_buf_base = NULL;
352
353 _IO_default_finish (fp, 0);
354}
355

source code of glibc/libio/strops.c