[v3,0/7] Fix some libm static issues
[platform/upstream/glibc.git] / libio / strops.c
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
33 void
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
67 void
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
73 void
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
80 int
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   if (flush_only)
137     return 0;
138   else
139     return c;
140 }
141 libc_hidden_def (_IO_str_overflow)
142
143 int
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 }
159 libc_hidden_def (_IO_str_underflow)
160
161 /* The size of the valid part of the buffer.  */
162
163 ssize_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
172 static int
173 enlarge_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 (newsize);
188   if (newbuf == NULL)
189     return 1;
190
191   if (oldbuf != NULL)
192     {
193       memcpy (newbuf, oldbuf, _IO_blen (fp));
194       free (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
234 static 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
250 off64_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, base, 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, base, 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 }
335 libc_hidden_def (_IO_str_seekoff)
336
337 int
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 }
344 libc_hidden_def (_IO_str_pbackfail)
345
346 void
347 _IO_str_finish (FILE *fp, int dummy)
348 {
349   if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
350     free (fp->_IO_buf_base);
351   fp->_IO_buf_base = NULL;
352
353   _IO_default_finish (fp, 0);
354 }