[v3,0/7] Fix some libm static issues
[platform/upstream/glibc.git] / libio / oldfileops.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 /* This is a compatibility file.  If we don't build the libc with
28    versioning don't compile this file.  */
29 #include <shlib-compat.h>
30 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
31
32 #define _IO_USE_OLD_IO_FILE
33 #include "libioP.h"
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41
42 /* An fstream can be in at most one of put mode, get mode, or putback mode.
43    Putback mode is a variant of get mode.
44
45    In a filebuf, there is only one current position, instead of two
46    separate get and put pointers.  In get mode, the current position
47    is that of gptr(); in put mode that of pptr().
48
49    The position in the buffer that corresponds to the position
50    in external file system is normally _IO_read_end, except in putback
51    mode, when it is _IO_save_end.
52    If the field _fb._offset is >= 0, it gives the offset in
53    the file as a whole corresponding to eGptr(). (?)
54
55    PUT MODE:
56    If a filebuf is in put mode, then all of _IO_read_ptr, _IO_read_end,
57    and _IO_read_base are equal to each other.  These are usually equal
58    to _IO_buf_base, though not necessarily if we have switched from
59    get mode to put mode.  (The reason is to maintain the invariant
60    that _IO_read_end corresponds to the external file position.)
61    _IO_write_base is non-NULL and usually equal to _IO_buf_base.
62    We also have _IO_write_end == _IO_buf_end, but only in fully buffered mode.
63    The un-flushed character are those between _IO_write_base and _IO_write_ptr.
64
65    GET MODE:
66    If a filebuf is in get or putback mode, eback() != egptr().
67    In get mode, the unread characters are between gptr() and egptr().
68    The OS file position corresponds to that of egptr().
69
70    PUTBACK MODE:
71    Putback mode is used to remember "excess" characters that have
72    been sputbackc'd in a separate putback buffer.
73    In putback mode, the get buffer points to the special putback buffer.
74    The unread characters are the characters between gptr() and egptr()
75    in the putback buffer, as well as the area between save_gptr()
76    and save_egptr(), which point into the original reserve buffer.
77    (The pointers save_gptr() and save_egptr() are the values
78    of gptr() and egptr() at the time putback mode was entered.)
79    The OS position corresponds to that of save_egptr().
80
81    LINE BUFFERED OUTPUT:
82    During line buffered output, _IO_write_base==base() && epptr()==base().
83    However, ptr() may be anywhere between base() and ebuf().
84    This forces a call to filebuf::overflow(int C) on every put.
85    If there is more space in the buffer, and C is not a '\n',
86    then C is inserted, and pptr() incremented.
87
88    UNBUFFERED STREAMS:
89    If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
90 */
91
92 #define CLOSED_FILEBUF_FLAGS \
93   (_IO_IS_FILEBUF+_IO_NO_READS+_IO_NO_WRITES+_IO_TIED_PUT_GET)
94
95
96 void
97 attribute_compat_text_section
98 _IO_old_file_init_internal (struct _IO_FILE_plus *fp)
99 {
100   /* POSIX.1 allows another file handle to be used to change the position
101      of our file descriptor.  Hence we actually don't know the actual
102      position before we do the first fseek (and until a following fflush). */
103   fp->file._old_offset = _IO_pos_BAD;
104   fp->file._flags |= CLOSED_FILEBUF_FLAGS;
105
106   _IO_link_in (fp);
107   fp->file._vtable_offset = ((int) sizeof (struct _IO_FILE)
108                              - (int) sizeof (struct _IO_FILE_complete));
109   fp->file._fileno = -1;
110
111   if (&_IO_stdin_used != NULL || !_IO_legacy_file ((FILE *) fp))
112     /* The object is dynamically allocated and large enough.  Initialize
113        the _mode element as well.  */
114     ((struct _IO_FILE_complete *) fp)->_mode = -1;
115 }
116
117 void
118 attribute_compat_text_section
119 _IO_old_file_init (struct _IO_FILE_plus *fp)
120 {
121   IO_set_accept_foreign_vtables (&_IO_vtable_check);
122   _IO_old_file_init_internal (fp);
123 }
124
125 int
126 attribute_compat_text_section
127 _IO_old_file_close_it (FILE *fp)
128 {
129   int write_status, close_status;
130   if (!_IO_file_is_open (fp))
131     return EOF;
132
133   write_status = _IO_old_do_flush (fp);
134
135   _IO_unsave_markers (fp);
136
137   close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0
138                   ? _IO_SYSCLOSE (fp) : 0);
139
140   /* Free buffer. */
141   _IO_setb (fp, NULL, NULL, 0);
142   _IO_setg (fp, NULL, NULL, NULL);
143   _IO_setp (fp, NULL, NULL);
144
145   _IO_un_link ((struct _IO_FILE_plus *) fp);
146   fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
147   fp->_fileno = -1;
148   fp->_old_offset = _IO_pos_BAD;
149
150   return close_status ? close_status : write_status;
151 }
152
153 void
154 attribute_compat_text_section
155 _IO_old_file_finish (FILE *fp, int dummy)
156 {
157   if (_IO_file_is_open (fp))
158     {
159       _IO_old_do_flush (fp);
160       if (!(fp->_flags & _IO_DELETE_DONT_CLOSE))
161         _IO_SYSCLOSE (fp);
162     }
163   _IO_default_finish (fp, 0);
164 }
165
166 FILE *
167 attribute_compat_text_section
168 _IO_old_file_fopen (FILE *fp, const char *filename, const char *mode)
169 {
170   int oflags = 0, omode;
171   int read_write, fdesc;
172   int oprot = 0666;
173   if (_IO_file_is_open (fp))
174     return 0;
175   switch (*mode++)
176     {
177     case 'r':
178       omode = O_RDONLY;
179       read_write = _IO_NO_WRITES;
180       break;
181     case 'w':
182       omode = O_WRONLY;
183       oflags = O_CREAT|O_TRUNC;
184       read_write = _IO_NO_READS;
185       break;
186     case 'a':
187       omode = O_WRONLY;
188       oflags = O_CREAT|O_APPEND;
189       read_write = _IO_NO_READS|_IO_IS_APPENDING;
190       break;
191     default:
192       __set_errno (EINVAL);
193       return NULL;
194     }
195   if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+'))
196     {
197       omode = O_RDWR;
198       read_write &= _IO_IS_APPENDING;
199     }
200   fdesc = __open (filename, omode|oflags, oprot);
201   if (fdesc < 0)
202     return NULL;
203   fp->_fileno = fdesc;
204   _IO_mask_flags (fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
205   if (read_write & _IO_IS_APPENDING)
206     if (_IO_SEEKOFF (fp, (off_t)0, _IO_seek_end, _IOS_INPUT|_IOS_OUTPUT)
207         == _IO_pos_BAD && errno != ESPIPE)
208       return NULL;
209   _IO_link_in ((struct _IO_FILE_plus *) fp);
210   return fp;
211 }
212
213 FILE *
214 attribute_compat_text_section
215 _IO_old_file_attach (FILE *fp, int fd)
216 {
217   if (_IO_file_is_open (fp))
218     return NULL;
219   fp->_fileno = fd;
220   fp->_flags &= ~(_IO_NO_READS+_IO_NO_WRITES);
221   fp->_flags |= _IO_DELETE_DONT_CLOSE;
222   /* Get the current position of the file. */
223   /* We have to do that since that may be junk. */
224   fp->_old_offset = _IO_pos_BAD;
225   if (_IO_SEEKOFF (fp, (off_t)0, _IO_seek_cur, _IOS_INPUT|_IOS_OUTPUT)
226       == _IO_pos_BAD && errno != ESPIPE)
227     return NULL;
228   return fp;
229 }
230
231 FILE *
232 attribute_compat_text_section
233 _IO_old_file_setbuf (FILE *fp, char *p, ssize_t len)
234 {
235     if (_IO_default_setbuf (fp, p, len) == NULL)
236       return NULL;
237
238     fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end
239       = fp->_IO_buf_base;
240     _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
241
242     return fp;
243 }
244
245 static int old_do_write (FILE *, const char *, size_t);
246
247 /* Write TO_DO bytes from DATA to FP.
248    Then mark FP as having empty buffers. */
249
250 int
251 attribute_compat_text_section
252 _IO_old_do_write (FILE *fp, const char *data, size_t to_do)
253 {
254   return (to_do == 0 || (size_t) old_do_write (fp, data, to_do) == to_do)
255          ? 0 : EOF;
256 }
257
258 static int
259 attribute_compat_text_section
260 old_do_write (FILE *fp, const char *data, size_t to_do)
261 {
262   size_t count;
263   if (fp->_flags & _IO_IS_APPENDING)
264     /* On a system without a proper O_APPEND implementation,
265        you would need to sys_seek(0, SEEK_END) here, but is
266        not needed nor desirable for Unix- or Posix-like systems.
267        Instead, just indicate that offset (before and after) is
268        unpredictable. */
269     fp->_old_offset = _IO_pos_BAD;
270   else if (fp->_IO_read_end != fp->_IO_write_base)
271     {
272       off_t new_pos
273         = _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);
274       if (new_pos == _IO_pos_BAD)
275         return 0;
276       fp->_old_offset = new_pos;
277     }
278   count = _IO_SYSWRITE (fp, data, to_do);
279   if (fp->_cur_column && count)
280     fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1;
281   _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
282   fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
283   fp->_IO_write_end = ((fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
284                        ? fp->_IO_buf_base : fp->_IO_buf_end);
285   return count;
286 }
287
288 int
289 attribute_compat_text_section
290 _IO_old_file_underflow (FILE *fp)
291 {
292   ssize_t count;
293
294   /* C99 requires EOF to be "sticky".  */
295   if (fp->_flags & _IO_EOF_SEEN)
296     return EOF;
297
298   if (fp->_flags & _IO_NO_READS)
299     {
300       fp->_flags |= _IO_ERR_SEEN;
301       __set_errno (EBADF);
302       return EOF;
303     }
304   if (fp->_IO_read_ptr < fp->_IO_read_end)
305     return *(unsigned char *) fp->_IO_read_ptr;
306
307   if (fp->_IO_buf_base == NULL)
308     {
309       /* Maybe we already have a push back pointer.  */
310       if (fp->_IO_save_base != NULL)
311         {
312           free (fp->_IO_save_base);
313           fp->_flags &= ~_IO_IN_BACKUP;
314         }
315       _IO_doallocbuf (fp);
316     }
317
318   /* Flush all line buffered files before reading. */
319   /* FIXME This can/should be moved to genops ?? */
320   if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
321     _IO_flush_all_linebuffered ();
322
323   _IO_switch_to_get_mode (fp);
324
325   /* This is very tricky. We have to adjust those
326      pointers before we call _IO_SYSREAD () since
327      we may longjump () out while waiting for
328      input. Those pointers may be screwed up. H.J. */
329   fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;
330   fp->_IO_read_end = fp->_IO_buf_base;
331   fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end
332     = fp->_IO_buf_base;
333
334   count = _IO_SYSREAD (fp, fp->_IO_buf_base,
335                        fp->_IO_buf_end - fp->_IO_buf_base);
336   if (count <= 0)
337     {
338       if (count == 0)
339         fp->_flags |= _IO_EOF_SEEN;
340       else
341         fp->_flags |= _IO_ERR_SEEN, count = 0;
342   }
343   fp->_IO_read_end += count;
344   if (count == 0)
345     return EOF;
346   if (fp->_old_offset != _IO_pos_BAD)
347     _IO_pos_adjust (fp->_old_offset, count);
348   return *(unsigned char *) fp->_IO_read_ptr;
349 }
350
351 int
352 attribute_compat_text_section
353 _IO_old_file_overflow (FILE *f, int ch)
354 {
355   if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
356     {
357       f->_flags |= _IO_ERR_SEEN;
358       __set_errno (EBADF);
359       return EOF;
360     }
361   /* If currently reading or no buffer allocated. */
362   if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0)
363     {
364       /* Allocate a buffer if needed. */
365       if (f->_IO_write_base == 0)
366         {
367           _IO_doallocbuf (f);
368           _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
369         }
370       /* Otherwise must be currently reading.
371          If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,
372          logically slide the buffer forwards one block (by setting the
373          read pointers to all point at the beginning of the block).  This
374          makes room for subsequent output.
375          Otherwise, set the read pointers to _IO_read_end (leaving that
376          alone, so it can continue to correspond to the external position). */
377       if (f->_IO_read_ptr == f->_IO_buf_end)
378         f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
379       f->_IO_write_ptr = f->_IO_read_ptr;
380       f->_IO_write_base = f->_IO_write_ptr;
381       f->_IO_write_end = f->_IO_buf_end;
382       f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
383
384       if (f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
385         f->_IO_write_end = f->_IO_write_ptr;
386       f->_flags |= _IO_CURRENTLY_PUTTING;
387     }
388   if (ch == EOF)
389     return _IO_old_do_flush (f);
390   if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */
391     if (_IO_old_do_flush (f) == EOF)
392       return EOF;
393   *f->_IO_write_ptr++ = ch;
394   if ((f->_flags & _IO_UNBUFFERED)
395       || ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
396     if (_IO_old_do_flush (f) == EOF)
397       return EOF;
398   return (unsigned char) ch;
399 }
400
401 int
402 attribute_compat_text_section
403 _IO_old_file_sync (FILE *fp)
404 {
405   ssize_t delta;
406   int retval = 0;
407
408   /*    char* ptr = cur_ptr(); */
409   if (fp->_IO_write_ptr > fp->_IO_write_base)
410     if (_IO_old_do_flush(fp)) return EOF;
411   delta = fp->_IO_read_ptr - fp->_IO_read_end;
412   if (delta != 0)
413     {
414 #ifdef TODO
415       if (_IO_in_backup (fp))
416         delta -= eGptr () - Gbase ();
417 #endif
418       off_t new_pos = _IO_SYSSEEK (fp, delta, 1);
419       if (new_pos != (off_t) EOF)
420         fp->_IO_read_end = fp->_IO_read_ptr;
421       else if (errno == ESPIPE)
422         ; /* Ignore error from unseekable devices. */
423       else
424         retval = EOF;
425     }
426   if (retval != EOF)
427     fp->_old_offset = _IO_pos_BAD;
428   /* FIXME: Cleanup - can this be shared? */
429   /*    setg(base(), ptr, ptr); */
430   return retval;
431 }
432
433 off64_t
434 attribute_compat_text_section
435 _IO_old_file_seekoff (FILE *fp, off64_t offset, int dir, int mode)
436 {
437   off_t result;
438   off64_t delta, new_offset;
439   long count;
440   /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
441      offset of the underlying file must be exact.  */
442   int must_be_exact = (fp->_IO_read_base == fp->_IO_read_end
443                        && fp->_IO_write_base == fp->_IO_write_ptr);
444
445   if (mode == 0)
446     dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
447
448   /* Flush unwritten characters.
449      (This may do an unneeded write if we seek within the buffer.
450      But to be able to switch to reading, we would need to set
451      egptr to pptr.  That can't be done in the current design,
452      which assumes file_ptr() is eGptr.  Anyway, since we probably
453      end up flushing when we close(), it doesn't make much difference.)
454      FIXME: simulate mem-mapped files. */
455
456   if (fp->_IO_write_ptr > fp->_IO_write_base || _IO_in_put_mode (fp))
457     if (_IO_switch_to_get_mode (fp))
458       return EOF;
459
460   if (fp->_IO_buf_base == NULL)
461     {
462       /* It could be that we already have a pushback buffer.  */
463       if (fp->_IO_read_base != NULL)
464         {
465           free (fp->_IO_read_base);
466           fp->_flags &= ~_IO_IN_BACKUP;
467         }
468       _IO_doallocbuf (fp);
469       _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
470       _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
471     }
472
473   switch (dir)
474     {
475     case _IO_seek_cur:
476       /* Adjust for read-ahead (bytes is buffer). */
477       offset -= fp->_IO_read_end - fp->_IO_read_ptr;
478       if (fp->_old_offset == _IO_pos_BAD)
479         goto dumb;
480       /* Make offset absolute, assuming current pointer is file_ptr(). */
481       offset += fp->_old_offset;
482
483       dir = _IO_seek_set;
484       break;
485     case _IO_seek_set:
486       break;
487     case _IO_seek_end:
488       {
489         struct __stat64_t64 st;
490         if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode))
491           {
492             offset += st.st_size;
493             dir = _IO_seek_set;
494           }
495         else
496           goto dumb;
497       }
498     }
499   /* At this point, dir==_IO_seek_set. */
500
501   /* If we are only interested in the current position we've found it now.  */
502   if (mode == 0)
503     return offset;
504
505   /* If destination is within current buffer, optimize: */
506   if (fp->_old_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
507       && !_IO_in_backup (fp))
508     {
509       /* Offset relative to start of main get area. */
510       off_t rel_offset = (offset - fp->_old_offset
511                           + (fp->_IO_read_end - fp->_IO_read_base));
512       if (rel_offset >= 0)
513         {
514           if (rel_offset <= fp->_IO_read_end - fp->_IO_read_base)
515             {
516               _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + rel_offset,
517                         fp->_IO_read_end);
518               _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
519               {
520                 _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
521                 goto resync;
522               }
523             }
524 #ifdef TODO
525             /* If we have streammarkers, seek forward by reading ahead. */
526             if (_IO_have_markers (fp))
527               {
528                 int to_skip = rel_offset
529                   - (fp->_IO_read_ptr - fp->_IO_read_base);
530                 if (ignore (to_skip) != to_skip)
531                   goto dumb;
532                 _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
533                 goto resync;
534               }
535 #endif
536         }
537 #ifdef TODO
538       if (rel_offset < 0 && rel_offset >= Bbase () - Bptr ())
539         {
540           if (!_IO_in_backup (fp))
541             _IO_switch_to_backup_area (fp);
542           gbump (fp->_IO_read_end + rel_offset - fp->_IO_read_ptr);
543           _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
544           goto resync;
545         }
546 #endif
547     }
548
549 #ifdef TODO
550   _IO_unsave_markers (fp);
551 #endif
552
553   if (fp->_flags & _IO_NO_READS)
554     goto dumb;
555
556   /* Try to seek to a block boundary, to improve kernel page management. */
557   new_offset = offset & ~(fp->_IO_buf_end - fp->_IO_buf_base - 1);
558   delta = offset - new_offset;
559   if (delta > fp->_IO_buf_end - fp->_IO_buf_base)
560     {
561       new_offset = offset;
562       delta = 0;
563     }
564   result = _IO_SYSSEEK (fp, new_offset, 0);
565   if (result < 0)
566     return EOF;
567   if (delta == 0)
568     count = 0;
569   else
570     {
571       count = _IO_SYSREAD (fp, fp->_IO_buf_base,
572                            (must_be_exact
573                             ? delta : fp->_IO_buf_end - fp->_IO_buf_base));
574       if (count < delta)
575         {
576           /* We weren't allowed to read, but try to seek the remainder. */
577           offset = count == EOF ? delta : delta-count;
578           dir = _IO_seek_cur;
579           goto dumb;
580         }
581     }
582   _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + delta,
583             fp->_IO_buf_base + count);
584   _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
585   fp->_old_offset = result + count;
586   _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
587   return offset;
588  dumb:
589
590   _IO_unsave_markers (fp);
591   result = _IO_SYSSEEK (fp, offset, dir);
592   if (result != EOF)
593     {
594       _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
595       fp->_old_offset = result;
596       _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
597       _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
598     }
599   return result;
600
601 resync:
602   /* We need to do it since it is possible that the file offset in
603      the kernel may be changed behind our back. It may happen when
604      we fopen a file and then do a fork. One process may access the
605      file and the kernel file offset will be changed. */
606   if (fp->_old_offset >= 0)
607     _IO_SYSSEEK (fp, fp->_old_offset, 0);
608
609   return offset;
610 }
611
612 ssize_t
613 attribute_compat_text_section
614 _IO_old_file_write (FILE *f, const void *data, ssize_t n)
615 {
616   ssize_t to_do = n;
617   while (to_do > 0)
618     {
619       ssize_t count = __write (f->_fileno, data, to_do);
620       if (count == EOF)
621         {
622           f->_flags |= _IO_ERR_SEEN;
623           break;
624         }
625       to_do -= count;
626       data = (void *) ((char *) data + count);
627     }
628   n -= to_do;
629   if (f->_old_offset >= 0)
630     f->_old_offset += n;
631   return n;
632 }
633
634 size_t
635 attribute_compat_text_section
636 _IO_old_file_xsputn (FILE *f, const void *data, size_t n)
637 {
638   const char *s = (char *) data;
639   size_t to_do = n;
640   int must_flush = 0;
641   size_t count = 0;
642
643   if (n <= 0)
644     return 0;
645   /* This is an optimized implementation.
646      If the amount to be written straddles a block boundary
647      (or the filebuf is unbuffered), use sys_write directly. */
648
649   /* First figure out how much space is available in the buffer. */
650   if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
651     {
652       count = f->_IO_buf_end - f->_IO_write_ptr;
653       if (count >= n)
654         {
655           const char *p;
656           for (p = s + n; p > s; )
657             {
658               if (*--p == '\n')
659                 {
660                   count = p - s + 1;
661                   must_flush = 1;
662                   break;
663                 }
664             }
665         }
666     }
667   else if (f->_IO_write_end > f->_IO_write_ptr)
668     count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
669
670   /* Then fill the buffer. */
671   if (count > 0)
672     {
673       if (count > to_do)
674         count = to_do;
675       if (count > 20)
676         {
677           f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
678           s += count;
679         }
680       else
681         {
682           char *p = f->_IO_write_ptr;
683           int i = (int) count;
684           while (--i >= 0)
685             *p++ = *s++;
686           f->_IO_write_ptr = p;
687         }
688       to_do -= count;
689     }
690   if (to_do + must_flush > 0)
691     {
692       size_t block_size, do_write;
693       /* Next flush the (full) buffer. */
694       if (__overflow (f, EOF) == EOF)
695         return to_do == 0 ? EOF : n - to_do;
696
697       /* Try to maintain alignment: write a whole number of blocks.
698          dont_write is what gets left over. */
699       block_size = f->_IO_buf_end - f->_IO_buf_base;
700       do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);
701
702       if (do_write)
703         {
704           count = old_do_write (f, s, do_write);
705           to_do -= count;
706           if (count < do_write)
707             return n - to_do;
708         }
709
710       /* Now write out the remainder.  Normally, this will fit in the
711          buffer, but it's somewhat messier for line-buffered files,
712          so we let _IO_default_xsputn handle the general case. */
713       if (to_do)
714         to_do -= _IO_default_xsputn (f, s+do_write, to_do);
715     }
716   return n - to_do;
717 }
718
719 compat_symbol (libc, _IO_old_do_write, _IO_do_write, GLIBC_2_0);
720 compat_symbol (libc, _IO_old_file_attach, _IO_file_attach, GLIBC_2_0);
721 compat_symbol (libc, _IO_old_file_close_it, _IO_file_close_it, GLIBC_2_0);
722 compat_symbol (libc, _IO_old_file_finish, _IO_file_finish, GLIBC_2_0);
723 compat_symbol (libc, _IO_old_file_fopen, _IO_file_fopen, GLIBC_2_0);
724 compat_symbol (libc, _IO_old_file_init, _IO_file_init, GLIBC_2_0);
725 compat_symbol (libc, _IO_old_file_setbuf, _IO_file_setbuf, GLIBC_2_0);
726 compat_symbol (libc, _IO_old_file_sync, _IO_file_sync, GLIBC_2_0);
727 compat_symbol (libc, _IO_old_file_overflow, _IO_file_overflow, GLIBC_2_0);
728 compat_symbol (libc, _IO_old_file_seekoff, _IO_file_seekoff, GLIBC_2_0);
729 compat_symbol (libc, _IO_old_file_underflow, _IO_file_underflow, GLIBC_2_0);
730 compat_symbol (libc, _IO_old_file_write, _IO_file_write, GLIBC_2_0);
731 compat_symbol (libc, _IO_old_file_xsputn, _IO_file_xsputn, GLIBC_2_0);
732
733 #endif