summaryrefslogtreecommitdiff
path: root/REORG.TODO/libio/fileops.c
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/libio/fileops.c')
-rw-r--r--REORG.TODO/libio/fileops.c1600
1 files changed, 1600 insertions, 0 deletions
diff --git a/REORG.TODO/libio/fileops.c b/REORG.TODO/libio/fileops.c
new file mode 100644
index 0000000000..c55e196dd8
--- /dev/null
+++ b/REORG.TODO/libio/fileops.c
@@ -0,0 +1,1600 @@
+/* Copyright (C) 1993-2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Written by Per Bothner <bothner@cygnus.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>.
+
+ As a special exception, if you link the code in this file with
+ files compiled with a GNU compiler to produce an executable,
+ that does not cause the resulting executable to be covered by
+ the GNU Lesser General Public License. This exception does not
+ however invalidate any other reasons why the executable file
+ might be covered by the GNU Lesser General Public License.
+ This exception applies to code released by its copyright holders
+ in files containing the exception. */
+
+
+#ifndef _POSIX_SOURCE
+# define _POSIX_SOURCE
+#endif
+#include "libioP.h"
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#if _LIBC
+# include "../wcsmbs/wcsmbsload.h"
+# include "../iconv/gconv_charset.h"
+# include "../iconv/gconv_int.h"
+# include <shlib-compat.h>
+# include <not-cancel.h>
+# include <kernel-features.h>
+#endif
+#ifndef errno
+extern int errno;
+#endif
+#ifndef __set_errno
+# define __set_errno(Val) errno = (Val)
+#endif
+
+
+#ifdef _LIBC
+# define open(Name, Flags, Prot) __open (Name, Flags, Prot)
+# define lseek(FD, Offset, Whence) __lseek (FD, Offset, Whence)
+# define read(FD, Buf, NBytes) __read (FD, Buf, NBytes)
+# define write(FD, Buf, NBytes) __write (FD, Buf, NBytes)
+#else
+# define _IO_new_do_write _IO_do_write
+# define _IO_new_file_attach _IO_file_attach
+# define _IO_new_file_close_it _IO_file_close_it
+# define _IO_new_file_finish _IO_file_finish
+# define _IO_new_file_fopen _IO_file_fopen
+# define _IO_new_file_init _IO_file_init
+# define _IO_new_file_setbuf _IO_file_setbuf
+# define _IO_new_file_sync _IO_file_sync
+# define _IO_new_file_overflow _IO_file_overflow
+# define _IO_new_file_seekoff _IO_file_seekoff
+# define _IO_new_file_underflow _IO_file_underflow
+# define _IO_new_file_write _IO_file_write
+# define _IO_new_file_xsputn _IO_file_xsputn
+#endif
+
+
+#ifdef _LIBC
+extern struct __gconv_trans_data __libio_translit attribute_hidden;
+#endif
+
+
+/* An fstream can be in at most one of put mode, get mode, or putback mode.
+ Putback mode is a variant of get mode.
+
+ In a filebuf, there is only one current position, instead of two
+ separate get and put pointers. In get mode, the current position
+ is that of gptr(); in put mode that of pptr().
+
+ The position in the buffer that corresponds to the position
+ in external file system is normally _IO_read_end, except in putback
+ mode, when it is _IO_save_end and also when the file is in append mode,
+ since switching from read to write mode automatically sends the position in
+ the external file system to the end of file.
+ If the field _fb._offset is >= 0, it gives the offset in
+ the file as a whole corresponding to eGptr(). (?)
+
+ PUT MODE:
+ If a filebuf is in put mode, then all of _IO_read_ptr, _IO_read_end,
+ and _IO_read_base are equal to each other. These are usually equal
+ to _IO_buf_base, though not necessarily if we have switched from
+ get mode to put mode. (The reason is to maintain the invariant
+ that _IO_read_end corresponds to the external file position.)
+ _IO_write_base is non-NULL and usually equal to _IO_buf_base.
+ We also have _IO_write_end == _IO_buf_end, but only in fully buffered mode.
+ The un-flushed character are those between _IO_write_base and _IO_write_ptr.
+
+ GET MODE:
+ If a filebuf is in get or putback mode, eback() != egptr().
+ In get mode, the unread characters are between gptr() and egptr().
+ The OS file position corresponds to that of egptr().
+
+ PUTBACK MODE:
+ Putback mode is used to remember "excess" characters that have
+ been sputbackc'd in a separate putback buffer.
+ In putback mode, the get buffer points to the special putback buffer.
+ The unread characters are the characters between gptr() and egptr()
+ in the putback buffer, as well as the area between save_gptr()
+ and save_egptr(), which point into the original reserve buffer.
+ (The pointers save_gptr() and save_egptr() are the values
+ of gptr() and egptr() at the time putback mode was entered.)
+ The OS position corresponds to that of save_egptr().
+
+ LINE BUFFERED OUTPUT:
+ During line buffered output, _IO_write_base==base() && epptr()==base().
+ However, ptr() may be anywhere between base() and ebuf().
+ This forces a call to filebuf::overflow(int C) on every put.
+ If there is more space in the buffer, and C is not a '\n',
+ then C is inserted, and pptr() incremented.
+
+ UNBUFFERED STREAMS:
+ If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
+*/
+
+#define CLOSED_FILEBUF_FLAGS \
+ (_IO_IS_FILEBUF+_IO_NO_READS+_IO_NO_WRITES+_IO_TIED_PUT_GET)
+
+
+void
+_IO_new_file_init_internal (struct _IO_FILE_plus *fp)
+{
+ /* POSIX.1 allows another file handle to be used to change the position
+ of our file descriptor. Hence we actually don't know the actual
+ position before we do the first fseek (and until a following fflush). */
+ fp->file._offset = _IO_pos_BAD;
+ fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS;
+
+ _IO_link_in (fp);
+ fp->file._fileno = -1;
+}
+
+/* External version of _IO_new_file_init_internal which switches off
+ vtable validation. */
+void
+_IO_new_file_init (struct _IO_FILE_plus *fp)
+{
+ IO_set_accept_foreign_vtables (&_IO_vtable_check);
+ _IO_new_file_init_internal (fp);
+}
+
+int
+_IO_new_file_close_it (_IO_FILE *fp)
+{
+ int write_status;
+ if (!_IO_file_is_open (fp))
+ return EOF;
+
+ if ((fp->_flags & _IO_NO_WRITES) == 0
+ && (fp->_flags & _IO_CURRENTLY_PUTTING) != 0)
+ write_status = _IO_do_flush (fp);
+ else
+ write_status = 0;
+
+ _IO_unsave_markers (fp);
+
+ int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0
+ ? _IO_SYSCLOSE (fp) : 0);
+
+ /* Free buffer. */
+#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
+ if (fp->_mode > 0)
+ {
+ if (_IO_have_wbackup (fp))
+ _IO_free_wbackup_area (fp);
+ _IO_wsetb (fp, NULL, NULL, 0);
+ _IO_wsetg (fp, NULL, NULL, NULL);
+ _IO_wsetp (fp, NULL, NULL);
+ }
+#endif
+ _IO_setb (fp, NULL, NULL, 0);
+ _IO_setg (fp, NULL, NULL, NULL);
+ _IO_setp (fp, NULL, NULL);
+
+ _IO_un_link ((struct _IO_FILE_plus *) fp);
+ fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
+ fp->_fileno = -1;
+ fp->_offset = _IO_pos_BAD;
+
+ return close_status ? close_status : write_status;
+}
+libc_hidden_ver (_IO_new_file_close_it, _IO_file_close_it)
+
+void
+_IO_new_file_finish (_IO_FILE *fp, int dummy)
+{
+ if (_IO_file_is_open (fp))
+ {
+ _IO_do_flush (fp);
+ if (!(fp->_flags & _IO_DELETE_DONT_CLOSE))
+ _IO_SYSCLOSE (fp);
+ }
+ _IO_default_finish (fp, 0);
+}
+libc_hidden_ver (_IO_new_file_finish, _IO_file_finish)
+
+_IO_FILE *
+_IO_file_open (_IO_FILE *fp, const char *filename, int posix_mode, int prot,
+ int read_write, int is32not64)
+{
+ int fdesc;
+#ifdef _LIBC
+ if (__glibc_unlikely (fp->_flags2 & _IO_FLAGS2_NOTCANCEL))
+ fdesc = open_not_cancel (filename,
+ posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot);
+ else
+ fdesc = open (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot);
+#else
+ fdesc = open (filename, posix_mode, prot);
+#endif
+ if (fdesc < 0)
+ return NULL;
+ fp->_fileno = fdesc;
+ _IO_mask_flags (fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
+ /* For append mode, send the file offset to the end of the file. Don't
+ update the offset cache though, since the file handle is not active. */
+ if ((read_write & (_IO_IS_APPENDING | _IO_NO_READS))
+ == (_IO_IS_APPENDING | _IO_NO_READS))
+ {
+ _IO_off64_t new_pos = _IO_SYSSEEK (fp, 0, _IO_seek_end);
+ if (new_pos == _IO_pos_BAD && errno != ESPIPE)
+ {
+ close_not_cancel (fdesc);
+ return NULL;
+ }
+ }
+ _IO_link_in ((struct _IO_FILE_plus *) fp);
+ return fp;
+}
+libc_hidden_def (_IO_file_open)
+
+_IO_FILE *
+_IO_new_file_fopen (_IO_FILE *fp, const char *filename, const char *mode,
+ int is32not64)
+{
+ int oflags = 0, omode;
+ int read_write;
+ int oprot = 0666;
+ int i;
+ _IO_FILE *result;
+#ifdef _LIBC
+ const char *cs;
+ const char *last_recognized;
+#endif
+
+ if (_IO_file_is_open (fp))
+ return 0;
+ switch (*mode)
+ {
+ case 'r':
+ omode = O_RDONLY;
+ read_write = _IO_NO_WRITES;
+ break;
+ case 'w':
+ omode = O_WRONLY;
+ oflags = O_CREAT|O_TRUNC;
+ read_write = _IO_NO_READS;
+ break;
+ case 'a':
+ omode = O_WRONLY;
+ oflags = O_CREAT|O_APPEND;
+ read_write = _IO_NO_READS|_IO_IS_APPENDING;
+ break;
+ default:
+ __set_errno (EINVAL);
+ return NULL;
+ }
+#ifdef _LIBC
+ last_recognized = mode;
+#endif
+ for (i = 1; i < 7; ++i)
+ {
+ switch (*++mode)
+ {
+ case '\0':
+ break;
+ case '+':
+ omode = O_RDWR;
+ read_write &= _IO_IS_APPENDING;
+#ifdef _LIBC
+ last_recognized = mode;
+#endif
+ continue;
+ case 'x':
+ oflags |= O_EXCL;
+#ifdef _LIBC
+ last_recognized = mode;
+#endif
+ continue;
+ case 'b':
+#ifdef _LIBC
+ last_recognized = mode;
+#endif
+ continue;
+ case 'm':
+ fp->_flags2 |= _IO_FLAGS2_MMAP;
+ continue;
+ case 'c':
+ fp->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+ continue;
+ case 'e':
+ oflags |= O_CLOEXEC;
+ fp->_flags2 |= _IO_FLAGS2_CLOEXEC;
+ continue;
+ default:
+ /* Ignore. */
+ continue;
+ }
+ break;
+ }
+
+ result = _IO_file_open (fp, filename, omode|oflags, oprot, read_write,
+ is32not64);
+
+ if (result != NULL)
+ {
+ /* Test whether the mode string specifies the conversion. */
+ cs = strstr (last_recognized + 1, ",ccs=");
+ if (cs != NULL)
+ {
+ /* Yep. Load the appropriate conversions and set the orientation
+ to wide. */
+ struct gconv_fcts fcts;
+ struct _IO_codecvt *cc;
+ char *endp = __strchrnul (cs + 5, ',');
+ char *ccs = malloc (endp - (cs + 5) + 3);
+
+ if (ccs == NULL)
+ {
+ int malloc_err = errno; /* Whatever malloc failed with. */
+ (void) _IO_file_close_it (fp);
+ __set_errno (malloc_err);
+ return NULL;
+ }
+
+ *((char *) __mempcpy (ccs, cs + 5, endp - (cs + 5))) = '\0';
+ strip (ccs, ccs);
+
+ if (__wcsmbs_named_conv (&fcts, ccs[2] == '\0'
+ ? upstr (ccs, cs + 5) : ccs) != 0)
+ {
+ /* Something went wrong, we cannot load the conversion modules.
+ This means we cannot proceed since the user explicitly asked
+ for these. */
+ (void) _IO_file_close_it (fp);
+ free (ccs);
+ __set_errno (EINVAL);
+ return NULL;
+ }
+
+ free (ccs);
+
+ assert (fcts.towc_nsteps == 1);
+ assert (fcts.tomb_nsteps == 1);
+
+ fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
+ fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base;
+
+ /* Clear the state. We start all over again. */
+ memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t));
+ memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t));
+
+ cc = fp->_codecvt = &fp->_wide_data->_codecvt;
+
+ /* The functions are always the same. */
+ *cc = __libio_codecvt;
+
+ cc->__cd_in.__cd.__nsteps = fcts.towc_nsteps;
+ cc->__cd_in.__cd.__steps = fcts.towc;
+
+ cc->__cd_in.__cd.__data[0].__invocation_counter = 0;
+ cc->__cd_in.__cd.__data[0].__internal_use = 1;
+ cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST;
+ cc->__cd_in.__cd.__data[0].__statep = &result->_wide_data->_IO_state;
+
+ cc->__cd_out.__cd.__nsteps = fcts.tomb_nsteps;
+ cc->__cd_out.__cd.__steps = fcts.tomb;
+
+ cc->__cd_out.__cd.__data[0].__invocation_counter = 0;
+ cc->__cd_out.__cd.__data[0].__internal_use = 1;
+ cc->__cd_out.__cd.__data[0].__flags
+ = __GCONV_IS_LAST | __GCONV_TRANSLIT;
+ cc->__cd_out.__cd.__data[0].__statep =
+ &result->_wide_data->_IO_state;
+
+ /* From now on use the wide character callback functions. */
+ _IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;
+
+ /* Set the mode now. */
+ result->_mode = 1;
+ }
+ }
+
+ return result;
+}
+libc_hidden_ver (_IO_new_file_fopen, _IO_file_fopen)
+
+_IO_FILE *
+_IO_new_file_attach (_IO_FILE *fp, int fd)
+{
+ if (_IO_file_is_open (fp))
+ return NULL;
+ fp->_fileno = fd;
+ fp->_flags &= ~(_IO_NO_READS+_IO_NO_WRITES);
+ fp->_flags |= _IO_DELETE_DONT_CLOSE;
+ /* Get the current position of the file. */
+ /* We have to do that since that may be junk. */
+ fp->_offset = _IO_pos_BAD;
+ int save_errno = errno;
+ if (_IO_SEEKOFF (fp, (_IO_off64_t)0, _IO_seek_cur, _IOS_INPUT|_IOS_OUTPUT)
+ == _IO_pos_BAD && errno != ESPIPE)
+ return NULL;
+ __set_errno (save_errno);
+ return fp;
+}
+libc_hidden_ver (_IO_new_file_attach, _IO_file_attach)
+
+_IO_FILE *
+_IO_new_file_setbuf (_IO_FILE *fp, char *p, _IO_ssize_t len)
+{
+ if (_IO_default_setbuf (fp, p, len) == NULL)
+ return NULL;
+
+ fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end
+ = fp->_IO_buf_base;
+ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
+
+ return fp;
+}
+libc_hidden_ver (_IO_new_file_setbuf, _IO_file_setbuf)
+
+
+_IO_FILE *
+_IO_file_setbuf_mmap (_IO_FILE *fp, char *p, _IO_ssize_t len)
+{
+ _IO_FILE *result;
+
+ /* Change the function table. */
+ _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+ fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+
+ /* And perform the normal operation. */
+ result = _IO_new_file_setbuf (fp, p, len);
+
+ /* If the call failed, restore to using mmap. */
+ if (result == NULL)
+ {
+ _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_mmap;
+ fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap;
+ }
+
+ return result;
+}
+
+static _IO_size_t new_do_write (_IO_FILE *, const char *, _IO_size_t);
+
+/* Write TO_DO bytes from DATA to FP.
+ Then mark FP as having empty buffers. */
+
+int
+_IO_new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
+{
+ return (to_do == 0
+ || (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF;
+}
+libc_hidden_ver (_IO_new_do_write, _IO_do_write)
+
+static
+_IO_size_t
+new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
+{
+ _IO_size_t count;
+ if (fp->_flags & _IO_IS_APPENDING)
+ /* On a system without a proper O_APPEND implementation,
+ you would need to sys_seek(0, SEEK_END) here, but is
+ not needed nor desirable for Unix- or Posix-like systems.
+ Instead, just indicate that offset (before and after) is
+ unpredictable. */
+ fp->_offset = _IO_pos_BAD;
+ else if (fp->_IO_read_end != fp->_IO_write_base)
+ {
+ _IO_off64_t new_pos
+ = _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);
+ if (new_pos == _IO_pos_BAD)
+ return 0;
+ fp->_offset = new_pos;
+ }
+ count = _IO_SYSWRITE (fp, data, to_do);
+ if (fp->_cur_column && count)
+ fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1;
+ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
+ fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
+ fp->_IO_write_end = (fp->_mode <= 0
+ && (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
+ ? fp->_IO_buf_base : fp->_IO_buf_end);
+ return count;
+}
+
+int
+_IO_new_file_underflow (_IO_FILE *fp)
+{
+ _IO_ssize_t count;
+#if 0
+ /* SysV does not make this test; take it out for compatibility */
+ if (fp->_flags & _IO_EOF_SEEN)
+ return (EOF);
+#endif
+
+ if (fp->_flags & _IO_NO_READS)
+ {
+ fp->_flags |= _IO_ERR_SEEN;
+ __set_errno (EBADF);
+ return EOF;
+ }
+ if (fp->_IO_read_ptr < fp->_IO_read_end)
+ return *(unsigned char *) fp->_IO_read_ptr;
+
+ if (fp->_IO_buf_base == NULL)
+ {
+ /* Maybe we already have a push back pointer. */
+ if (fp->_IO_save_base != NULL)
+ {
+ free (fp->_IO_save_base);
+ fp->_flags &= ~_IO_IN_BACKUP;
+ }
+ _IO_doallocbuf (fp);
+ }
+
+ /* Flush all line buffered files before reading. */
+ /* FIXME This can/should be moved to genops ?? */
+ if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
+ {
+#if 0
+ _IO_flush_all_linebuffered ();
+#else
+ /* We used to flush all line-buffered stream. This really isn't
+ required by any standard. My recollection is that
+ traditional Unix systems did this for stdout. stderr better
+ not be line buffered. So we do just that here
+ explicitly. --drepper */
+ _IO_acquire_lock (_IO_stdout);
+
+ if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))
+ == (_IO_LINKED | _IO_LINE_BUF))
+ _IO_OVERFLOW (_IO_stdout, EOF);
+
+ _IO_release_lock (_IO_stdout);
+#endif
+ }
+
+ _IO_switch_to_get_mode (fp);
+
+ /* This is very tricky. We have to adjust those
+ pointers before we call _IO_SYSREAD () since
+ we may longjump () out while waiting for
+ input. Those pointers may be screwed up. H.J. */
+ fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base;
+ fp->_IO_read_end = fp->_IO_buf_base;
+ fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end
+ = fp->_IO_buf_base;
+
+ count = _IO_SYSREAD (fp, fp->_IO_buf_base,
+ fp->_IO_buf_end - fp->_IO_buf_base);
+ if (count <= 0)
+ {
+ if (count == 0)
+ fp->_flags |= _IO_EOF_SEEN;
+ else
+ fp->_flags |= _IO_ERR_SEEN, count = 0;
+ }
+ fp->_IO_read_end += count;
+ if (count == 0)
+ {
+ /* If a stream is read to EOF, the calling application may switch active
+ handles. As a result, our offset cache would no longer be valid, so
+ unset it. */
+ fp->_offset = _IO_pos_BAD;
+ return EOF;
+ }
+ if (fp->_offset != _IO_pos_BAD)
+ _IO_pos_adjust (fp->_offset, count);
+ return *(unsigned char *) fp->_IO_read_ptr;
+}
+libc_hidden_ver (_IO_new_file_underflow, _IO_file_underflow)
+
+/* Guts of underflow callback if we mmap the file. This stats the file and
+ updates the stream state to match. In the normal case we return zero.
+ If the file is no longer eligible for mmap, its jump tables are reset to
+ the vanilla ones and we return nonzero. */
+static int
+mmap_remap_check (_IO_FILE *fp)
+{
+ struct stat64 st;
+
+ if (_IO_SYSSTAT (fp, &st) == 0
+ && S_ISREG (st.st_mode) && st.st_size != 0
+ /* Limit the file size to 1MB for 32-bit machines. */
+ && (sizeof (ptrdiff_t) > 4 || st.st_size < 1*1024*1024))
+ {
+ const size_t pagesize = __getpagesize ();
+# define ROUNDED(x) (((x) + pagesize - 1) & ~(pagesize - 1))
+ if (ROUNDED (st.st_size) < ROUNDED (fp->_IO_buf_end
+ - fp->_IO_buf_base))
+ {
+ /* We can trim off some pages past the end of the file. */
+ (void) __munmap (fp->_IO_buf_base + ROUNDED (st.st_size),
+ ROUNDED (fp->_IO_buf_end - fp->_IO_buf_base)
+ - ROUNDED (st.st_size));
+ fp->_IO_buf_end = fp->_IO_buf_base + st.st_size;
+ }
+ else if (ROUNDED (st.st_size) > ROUNDED (fp->_IO_buf_end
+ - fp->_IO_buf_base))
+ {
+ /* The file added some pages. We need to remap it. */
+ void *p;
+#ifdef _G_HAVE_MREMAP
+ p = __mremap (fp->_IO_buf_base, ROUNDED (fp->_IO_buf_end
+ - fp->_IO_buf_base),
+ ROUNDED (st.st_size), MREMAP_MAYMOVE);
+ if (p == MAP_FAILED)
+ {
+ (void) __munmap (fp->_IO_buf_base,
+ fp->_IO_buf_end - fp->_IO_buf_base);
+ goto punt;
+ }
+#else
+ (void) __munmap (fp->_IO_buf_base,
+ fp->_IO_buf_end - fp->_IO_buf_base);
+ p = __mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
+ fp->_fileno, 0);
+ if (p == MAP_FAILED)
+ goto punt;
+#endif
+ fp->_IO_buf_base = p;
+ fp->_IO_buf_end = fp->_IO_buf_base + st.st_size;
+ }
+ else
+ {
+ /* The number of pages didn't change. */
+ fp->_IO_buf_end = fp->_IO_buf_base + st.st_size;
+ }
+# undef ROUNDED
+
+ fp->_offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+ _IO_setg (fp, fp->_IO_buf_base,
+ fp->_offset < fp->_IO_buf_end - fp->_IO_buf_base
+ ? fp->_IO_buf_base + fp->_offset : fp->_IO_buf_end,
+ fp->_IO_buf_end);
+
+ /* If we are already positioned at or past the end of the file, don't
+ change the current offset. If not, seek past what we have mapped,
+ mimicking the position left by a normal underflow reading into its
+ buffer until EOF. */
+
+ if (fp->_offset < fp->_IO_buf_end - fp->_IO_buf_base)
+ {
+ if (__lseek64 (fp->_fileno, fp->_IO_buf_end - fp->_IO_buf_base,
+ SEEK_SET)
+ != fp->_IO_buf_end - fp->_IO_buf_base)
+ fp->_flags |= _IO_ERR_SEEN;
+ else
+ fp->_offset = fp->_IO_buf_end - fp->_IO_buf_base;
+ }
+
+ return 0;
+ }
+ else
+ {
+ /* Life is no longer good for mmap. Punt it. */
+ (void) __munmap (fp->_IO_buf_base,
+ fp->_IO_buf_end - fp->_IO_buf_base);
+ punt:
+ fp->_IO_buf_base = fp->_IO_buf_end = NULL;
+ _IO_setg (fp, NULL, NULL, NULL);
+ if (fp->_mode <= 0)
+ _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+ else
+ _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps;
+ fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+
+ return 1;
+ }
+}
+
+/* Special callback replacing the underflow callbacks if we mmap the file. */
+int
+_IO_file_underflow_mmap (_IO_FILE *fp)
+{
+ if (fp->_IO_read_ptr < fp->_IO_read_end)
+ return *(unsigned char *) fp->_IO_read_ptr;
+
+ if (__glibc_unlikely (mmap_remap_check (fp)))
+ /* We punted to the regular file functions. */
+ return _IO_UNDERFLOW (fp);
+
+ if (fp->_IO_read_ptr < fp->_IO_read_end)
+ return *(unsigned char *) fp->_IO_read_ptr;
+
+ fp->_flags |= _IO_EOF_SEEN;
+ return EOF;
+}
+
+static void
+decide_maybe_mmap (_IO_FILE *fp)
+{
+ /* We use the file in read-only mode. This could mean we can
+ mmap the file and use it without any copying. But not all
+ file descriptors are for mmap-able objects and on 32-bit
+ machines we don't want to map files which are too large since
+ this would require too much virtual memory. */
+ struct stat64 st;
+
+ if (_IO_SYSSTAT (fp, &st) == 0
+ && S_ISREG (st.st_mode) && st.st_size != 0
+ /* Limit the file size to 1MB for 32-bit machines. */
+ && (sizeof (ptrdiff_t) > 4 || st.st_size < 1*1024*1024)
+ /* Sanity check. */
+ && (fp->_offset == _IO_pos_BAD || fp->_offset <= st.st_size))
+ {
+ /* Try to map the file. */
+ void *p;
+
+ p = __mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED, fp->_fileno, 0);
+ if (p != MAP_FAILED)
+ {
+ /* OK, we managed to map the file. Set the buffer up and use a
+ special jump table with simplified underflow functions which
+ never tries to read anything from the file. */
+
+ if (__lseek64 (fp->_fileno, st.st_size, SEEK_SET) != st.st_size)
+ {
+ (void) __munmap (p, st.st_size);
+ fp->_offset = _IO_pos_BAD;
+ }
+ else
+ {
+ _IO_setb (fp, p, (char *) p + st.st_size, 0);
+
+ if (fp->_offset == _IO_pos_BAD)
+ fp->_offset = 0;
+
+ _IO_setg (fp, p, p + fp->_offset, p + st.st_size);
+ fp->_offset = st.st_size;
+
+ if (fp->_mode <= 0)
+ _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_mmap;
+ else
+ _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_mmap;
+ fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_mmap;
+
+ return;
+ }
+ }
+ }
+
+ /* We couldn't use mmap, so revert to the vanilla file operations. */
+
+ if (fp->_mode <= 0)
+ _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
+ else
+ _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps;
+ fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+}
+
+int
+_IO_file_underflow_maybe_mmap (_IO_FILE *fp)
+{
+ /* This is the first read attempt. Choose mmap or vanilla operations
+ and then punt to the chosen underflow routine. */
+ decide_maybe_mmap (fp);
+ return _IO_UNDERFLOW (fp);
+}
+
+
+int
+_IO_new_file_overflow (_IO_FILE *f, int ch)
+{
+ if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
+ {
+ f->_flags |= _IO_ERR_SEEN;
+ __set_errno (EBADF);
+ return EOF;
+ }
+ /* If currently reading or no buffer allocated. */
+ if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)
+ {
+ /* Allocate a buffer if needed. */
+ if (f->_IO_write_base == NULL)
+ {
+ _IO_doallocbuf (f);
+ _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
+ }
+ /* Otherwise must be currently reading.
+ If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,
+ logically slide the buffer forwards one block (by setting the
+ read pointers to all point at the beginning of the block). This
+ makes room for subsequent output.
+ Otherwise, set the read pointers to _IO_read_end (leaving that
+ alone, so it can continue to correspond to the external position). */
+ if (__glibc_unlikely (_IO_in_backup (f)))
+ {
+ size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;
+ _IO_free_backup_area (f);
+ f->_IO_read_base -= MIN (nbackup,
+ f->_IO_read_base - f->_IO_buf_base);
+ f->_IO_read_ptr = f->_IO_read_base;
+ }
+
+ if (f->_IO_read_ptr == f->_IO_buf_end)
+ f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
+ f->_IO_write_ptr = f->_IO_read_ptr;
+ f->_IO_write_base = f->_IO_write_ptr;
+ f->_IO_write_end = f->_IO_buf_end;
+ f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
+
+ f->_flags |= _IO_CURRENTLY_PUTTING;
+ if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
+ f->_IO_write_end = f->_IO_write_ptr;
+ }
+ if (ch == EOF)
+ return _IO_do_write (f, f->_IO_write_base,
+ f->_IO_write_ptr - f->_IO_write_base);
+ if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */
+ if (_IO_do_flush (f) == EOF)
+ return EOF;
+ *f->_IO_write_ptr++ = ch;
+ if ((f->_flags & _IO_UNBUFFERED)
+ || ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
+ if (_IO_do_write (f, f->_IO_write_base,
+ f->_IO_write_ptr - f->_IO_write_base) == EOF)
+ return EOF;
+ return (unsigned char) ch;
+}
+libc_hidden_ver (_IO_new_file_overflow, _IO_file_overflow)
+
+int
+_IO_new_file_sync (_IO_FILE *fp)
+{
+ _IO_ssize_t delta;
+ int retval = 0;
+
+ /* char* ptr = cur_ptr(); */
+ if (fp->_IO_write_ptr > fp->_IO_write_base)
+ if (_IO_do_flush(fp)) return EOF;
+ delta = fp->_IO_read_ptr - fp->_IO_read_end;
+ if (delta != 0)
+ {
+#ifdef TODO
+ if (_IO_in_backup (fp))
+ delta -= eGptr () - Gbase ();
+#endif
+ _IO_off64_t new_pos = _IO_SYSSEEK (fp, delta, 1);
+ if (new_pos != (_IO_off64_t) EOF)
+ fp->_IO_read_end = fp->_IO_read_ptr;
+#ifdef ESPIPE
+ else if (errno == ESPIPE)
+ ; /* Ignore error from unseekable devices. */
+#endif
+ else
+ retval = EOF;
+ }
+ if (retval != EOF)
+ fp->_offset = _IO_pos_BAD;
+ /* FIXME: Cleanup - can this be shared? */
+ /* setg(base(), ptr, ptr); */
+ return retval;
+}
+libc_hidden_ver (_IO_new_file_sync, _IO_file_sync)
+
+static int
+_IO_file_sync_mmap (_IO_FILE *fp)
+{
+ if (fp->_IO_read_ptr != fp->_IO_read_end)
+ {
+#ifdef TODO
+ if (_IO_in_backup (fp))
+ delta -= eGptr () - Gbase ();
+#endif
+ if (__lseek64 (fp->_fileno, fp->_IO_read_ptr - fp->_IO_buf_base,
+ SEEK_SET)
+ != fp->_IO_read_ptr - fp->_IO_buf_base)
+ {
+ fp->_flags |= _IO_ERR_SEEN;
+ return EOF;
+ }
+ }
+ fp->_offset = fp->_IO_read_ptr - fp->_IO_buf_base;
+ fp->_IO_read_end = fp->_IO_read_ptr = fp->_IO_read_base;
+ return 0;
+}
+
+/* ftell{,o} implementation. The only time we modify the state of the stream
+ is when we have unflushed writes. In that case we seek to the end and
+ record that offset in the stream object. */
+static _IO_off64_t
+do_ftell (_IO_FILE *fp)
+{
+ _IO_off64_t result, offset = 0;
+
+ /* No point looking at unflushed data if we haven't allocated buffers
+ yet. */
+ if (fp->_IO_buf_base != NULL)
+ {
+ bool unflushed_writes = fp->_IO_write_ptr > fp->_IO_write_base;
+
+ bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING;
+
+ /* When we have unflushed writes in append mode, seek to the end of the
+ file and record that offset. This is the only time we change the file
+ stream state and it is safe since the file handle is active. */
+ if (unflushed_writes && append_mode)
+ {
+ result = _IO_SYSSEEK (fp, 0, _IO_seek_end);
+ if (result == _IO_pos_BAD)
+ return EOF;
+ else
+ fp->_offset = result;
+ }
+
+ /* Adjust for unflushed data. */
+ if (!unflushed_writes)
+ offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+ /* We don't trust _IO_read_end to represent the current file offset when
+ writing in append mode because the value would have to be shifted to
+ the end of the file during a flush. Use the write base instead, along
+ with the new offset we got above when we did a seek to the end of the
+ file. */
+ else if (append_mode)
+ offset += fp->_IO_write_ptr - fp->_IO_write_base;
+ /* For all other modes, _IO_read_end represents the file offset. */
+ else
+ offset += fp->_IO_write_ptr - fp->_IO_read_end;
+ }
+
+ if (fp->_offset != _IO_pos_BAD)
+ result = fp->_offset;
+ else
+ result = _IO_SYSSEEK (fp, 0, _IO_seek_cur);
+
+ if (result == EOF)
+ return result;
+
+ result += offset;
+
+ if (result < 0)
+ {
+ __set_errno (EINVAL);
+ return EOF;
+ }
+
+ return result;
+}
+
+_IO_off64_t
+_IO_new_file_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
+{
+ _IO_off64_t result;
+ _IO_off64_t delta, new_offset;
+ long count;
+
+ /* Short-circuit into a separate function. We don't want to mix any
+ functionality and we don't want to touch anything inside the FILE
+ object. */
+ if (mode == 0)
+ return do_ftell (fp);
+
+ /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
+ offset of the underlying file must be exact. */
+ int must_be_exact = (fp->_IO_read_base == fp->_IO_read_end
+ && fp->_IO_write_base == fp->_IO_write_ptr);
+
+ bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
+ || _IO_in_put_mode (fp));
+
+ /* Flush unwritten characters.
+ (This may do an unneeded write if we seek within the buffer.
+ But to be able to switch to reading, we would need to set
+ egptr to pptr. That can't be done in the current design,
+ which assumes file_ptr() is eGptr. Anyway, since we probably
+ end up flushing when we close(), it doesn't make much difference.)
+ FIXME: simulate mem-mapped files. */
+ if (was_writing && _IO_switch_to_get_mode (fp))
+ return EOF;
+
+ if (fp->_IO_buf_base == NULL)
+ {
+ /* It could be that we already have a pushback buffer. */
+ if (fp->_IO_read_base != NULL)
+ {
+ free (fp->_IO_read_base);
+ fp->_flags &= ~_IO_IN_BACKUP;
+ }
+ _IO_doallocbuf (fp);
+ _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
+ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
+ }
+
+ switch (dir)
+ {
+ case _IO_seek_cur:
+ /* Adjust for read-ahead (bytes is buffer). */
+ offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+
+ if (fp->_offset == _IO_pos_BAD)
+ goto dumb;
+ /* Make offset absolute, assuming current pointer is file_ptr(). */
+ offset += fp->_offset;
+ if (offset < 0)
+ {
+ __set_errno (EINVAL);
+ return EOF;
+ }
+
+ dir = _IO_seek_set;
+ break;
+ case _IO_seek_set:
+ break;
+ case _IO_seek_end:
+ {
+ struct stat64 st;
+ if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode))
+ {
+ offset += st.st_size;
+ dir = _IO_seek_set;
+ }
+ else
+ goto dumb;
+ }
+ }
+ /* At this point, dir==_IO_seek_set. */
+
+ /* If destination is within current buffer, optimize: */
+ if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
+ && !_IO_in_backup (fp))
+ {
+ _IO_off64_t start_offset = (fp->_offset
+ - (fp->_IO_read_end - fp->_IO_buf_base));
+ if (offset >= start_offset && offset < fp->_offset)
+ {
+ _IO_setg (fp, fp->_IO_buf_base,
+ fp->_IO_buf_base + (offset - start_offset),
+ fp->_IO_read_end);
+ _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
+
+ _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
+ goto resync;
+ }
+ }
+
+ if (fp->_flags & _IO_NO_READS)
+ goto dumb;
+
+ /* Try to seek to a block boundary, to improve kernel page management. */
+ new_offset = offset & ~(fp->_IO_buf_end - fp->_IO_buf_base - 1);
+ delta = offset - new_offset;
+ if (delta > fp->_IO_buf_end - fp->_IO_buf_base)
+ {
+ new_offset = offset;
+ delta = 0;
+ }
+ result = _IO_SYSSEEK (fp, new_offset, 0);
+ if (result < 0)
+ return EOF;
+ if (delta == 0)
+ count = 0;
+ else
+ {
+ count = _IO_SYSREAD (fp, fp->_IO_buf_base,
+ (must_be_exact
+ ? delta : fp->_IO_buf_end - fp->_IO_buf_base));
+ if (count < delta)
+ {
+ /* We weren't allowed to read, but try to seek the remainder. */
+ offset = count == EOF ? delta : delta-count;
+ dir = _IO_seek_cur;
+ goto dumb;
+ }
+ }
+ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + delta,
+ fp->_IO_buf_base + count);
+ _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
+ fp->_offset = result + count;
+ _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
+ return offset;
+ dumb:
+
+ _IO_unsave_markers (fp);
+ result = _IO_SYSSEEK (fp, offset, dir);
+ if (result != EOF)
+ {
+ _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
+ fp->_offset = result;
+ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
+ _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
+ }
+ return result;
+
+resync:
+ /* We need to do it since it is possible that the file offset in
+ the kernel may be changed behind our back. It may happen when
+ we fopen a file and then do a fork. One process may access the
+ file and the kernel file offset will be changed. */
+ if (fp->_offset >= 0)
+ _IO_SYSSEEK (fp, fp->_offset, 0);
+
+ return offset;
+}
+libc_hidden_ver (_IO_new_file_seekoff, _IO_file_seekoff)
+
+_IO_off64_t
+_IO_file_seekoff_mmap (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
+{
+ _IO_off64_t result;
+
+ /* If we are only interested in the current position, calculate it and
+ return right now. This calculation does the right thing when we are
+ using a pushback buffer, but in the usual case has the same value as
+ (fp->_IO_read_ptr - fp->_IO_buf_base). */
+ if (mode == 0)
+ return fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr);
+
+ switch (dir)
+ {
+ case _IO_seek_cur:
+ /* Adjust for read-ahead (bytes is buffer). */
+ offset += fp->_IO_read_ptr - fp->_IO_read_base;
+ break;
+ case _IO_seek_set:
+ break;
+ case _IO_seek_end:
+ offset += fp->_IO_buf_end - fp->_IO_buf_base;
+ break;
+ }
+ /* At this point, dir==_IO_seek_set. */
+
+ if (offset < 0)
+ {
+ /* No negative offsets are valid. */
+ __set_errno (EINVAL);
+ return EOF;
+ }
+
+ result = _IO_SYSSEEK (fp, offset, 0);
+ if (result < 0)
+ return EOF;
+
+ if (offset > fp->_IO_buf_end - fp->_IO_buf_base)
+ /* One can fseek arbitrarily past the end of the file
+ and it is meaningless until one attempts to read.
+ Leave the buffer pointers in EOF state until underflow. */
+ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_end, fp->_IO_buf_end);
+ else
+ /* Adjust the read pointers to match the file position,
+ but so the next read attempt will call underflow. */
+ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + offset,
+ fp->_IO_buf_base + offset);
+
+ fp->_offset = result;
+
+ _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
+
+ return offset;
+}
+
+static _IO_off64_t
+_IO_file_seekoff_maybe_mmap (_IO_FILE *fp, _IO_off64_t offset, int dir,
+ int mode)
+{
+ /* We only get here when we haven't tried to read anything yet.
+ So there is nothing more useful for us to do here than just
+ the underlying lseek call. */
+
+ _IO_off64_t result = _IO_SYSSEEK (fp, offset, dir);
+ if (result < 0)
+ return EOF;
+
+ fp->_offset = result;
+ return result;
+}
+
+_IO_ssize_t
+_IO_file_read (_IO_FILE *fp, void *buf, _IO_ssize_t size)
+{
+ return (__builtin_expect (fp->_flags2 & _IO_FLAGS2_NOTCANCEL, 0)
+ ? read_not_cancel (fp->_fileno, buf, size)
+ : read (fp->_fileno, buf, size));
+}
+libc_hidden_def (_IO_file_read)
+
+_IO_off64_t
+_IO_file_seek (_IO_FILE *fp, _IO_off64_t offset, int dir)
+{
+ return __lseek64 (fp->_fileno, offset, dir);
+}
+libc_hidden_def (_IO_file_seek)
+
+int
+_IO_file_stat (_IO_FILE *fp, void *st)
+{
+ return __fxstat64 (_STAT_VER, fp->_fileno, (struct stat64 *) st);
+}
+libc_hidden_def (_IO_file_stat)
+
+int
+_IO_file_close_mmap (_IO_FILE *fp)
+{
+ /* In addition to closing the file descriptor we have to unmap the file. */
+ (void) __munmap (fp->_IO_buf_base, fp->_IO_buf_end - fp->_IO_buf_base);
+ fp->_IO_buf_base = fp->_IO_buf_end = NULL;
+ /* Cancelling close should be avoided if possible since it leaves an
+ unrecoverable state behind. */
+ return close_not_cancel (fp->_fileno);
+}
+
+int
+_IO_file_close (_IO_FILE *fp)
+{
+ /* Cancelling close should be avoided if possible since it leaves an
+ unrecoverable state behind. */
+ return close_not_cancel (fp->_fileno);
+}
+libc_hidden_def (_IO_file_close)
+
+_IO_ssize_t
+_IO_new_file_write (_IO_FILE *f, const void *data, _IO_ssize_t n)
+{
+ _IO_ssize_t to_do = n;
+ while (to_do > 0)
+ {
+ _IO_ssize_t count = (__builtin_expect (f->_flags2
+ & _IO_FLAGS2_NOTCANCEL, 0)
+ ? write_not_cancel (f->_fileno, data, to_do)
+ : write (f->_fileno, data, to_do));
+ if (count < 0)
+ {
+ f->_flags |= _IO_ERR_SEEN;
+ break;
+ }
+ to_do -= count;
+ data = (void *) ((char *) data + count);
+ }
+ n -= to_do;
+ if (f->_offset >= 0)
+ f->_offset += n;
+ return n;
+}
+
+_IO_size_t
+_IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
+{
+ const char *s = (const char *) data;
+ _IO_size_t to_do = n;
+ int must_flush = 0;
+ _IO_size_t count = 0;
+
+ if (n <= 0)
+ return 0;
+ /* This is an optimized implementation.
+ If the amount to be written straddles a block boundary
+ (or the filebuf is unbuffered), use sys_write directly. */
+
+ /* First figure out how much space is available in the buffer. */
+ if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
+ {
+ count = f->_IO_buf_end - f->_IO_write_ptr;
+ if (count >= n)
+ {
+ const char *p;
+ for (p = s + n; p > s; )
+ {
+ if (*--p == '\n')
+ {
+ count = p - s + 1;
+ must_flush = 1;
+ break;
+ }
+ }
+ }
+ }
+ else if (f->_IO_write_end > f->_IO_write_ptr)
+ count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
+
+ /* Then fill the buffer. */
+ if (count > 0)
+ {
+ if (count > to_do)
+ count = to_do;
+#ifdef _LIBC
+ f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
+#else
+ memcpy (f->_IO_write_ptr, s, count);
+ f->_IO_write_ptr += count;
+#endif
+ s += count;
+ to_do -= count;
+ }
+ if (to_do + must_flush > 0)
+ {
+ _IO_size_t block_size, do_write;
+ /* Next flush the (full) buffer. */
+ if (_IO_OVERFLOW (f, EOF) == EOF)
+ /* If nothing else has to be written we must not signal the
+ caller that everything has been written. */
+ return to_do == 0 ? EOF : n - to_do;
+
+ /* Try to maintain alignment: write a whole number of blocks. */
+ block_size = f->_IO_buf_end - f->_IO_buf_base;
+ do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);
+
+ if (do_write)
+ {
+ count = new_do_write (f, s, do_write);
+ to_do -= count;
+ if (count < do_write)
+ return n - to_do;
+ }
+
+ /* Now write out the remainder. Normally, this will fit in the
+ buffer, but it's somewhat messier for line-buffered files,
+ so we let _IO_default_xsputn handle the general case. */
+ if (to_do)
+ to_do -= _IO_default_xsputn (f, s+do_write, to_do);
+ }
+ return n - to_do;
+}
+libc_hidden_ver (_IO_new_file_xsputn, _IO_file_xsputn)
+
+_IO_size_t
+_IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
+{
+ _IO_size_t want, have;
+ _IO_ssize_t count;
+ char *s = data;
+
+ want = n;
+
+ if (fp->_IO_buf_base == NULL)
+ {
+ /* Maybe we already have a push back pointer. */
+ if (fp->_IO_save_base != NULL)
+ {
+ free (fp->_IO_save_base);
+ fp->_flags &= ~_IO_IN_BACKUP;
+ }
+ _IO_doallocbuf (fp);
+ }
+
+ while (want > 0)
+ {
+ have = fp->_IO_read_end - fp->_IO_read_ptr;
+ if (want <= have)
+ {
+ memcpy (s, fp->_IO_read_ptr, want);
+ fp->_IO_read_ptr += want;
+ want = 0;
+ }
+ else
+ {
+ if (have > 0)
+ {
+#ifdef _LIBC
+ s = __mempcpy (s, fp->_IO_read_ptr, have);
+#else
+ memcpy (s, fp->_IO_read_ptr, have);
+ s += have;
+#endif
+ want -= have;
+ fp->_IO_read_ptr += have;
+ }
+
+ /* Check for backup and repeat */
+ if (_IO_in_backup (fp))
+ {
+ _IO_switch_to_main_get_area (fp);
+ continue;
+ }
+
+ /* If we now want less than a buffer, underflow and repeat
+ the copy. Otherwise, _IO_SYSREAD directly to
+ the user buffer. */
+ if (fp->_IO_buf_base
+ && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base))
+ {
+ if (__underflow (fp) == EOF)
+ break;
+
+ continue;
+ }
+
+ /* These must be set before the sysread as we might longjmp out
+ waiting for input. */
+ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
+ _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
+
+ /* Try to maintain alignment: read a whole number of blocks. */
+ count = want;
+ if (fp->_IO_buf_base)
+ {
+ _IO_size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base;
+ if (block_size >= 128)
+ count -= want % block_size;
+ }
+
+ count = _IO_SYSREAD (fp, s, count);
+ if (count <= 0)
+ {
+ if (count == 0)
+ fp->_flags |= _IO_EOF_SEEN;
+ else
+ fp->_flags |= _IO_ERR_SEEN;
+
+ break;
+ }
+
+ s += count;
+ want -= count;
+ if (fp->_offset != _IO_pos_BAD)
+ _IO_pos_adjust (fp->_offset, count);
+ }
+ }
+
+ return n - want;
+}
+libc_hidden_def (_IO_file_xsgetn)
+
+static _IO_size_t
+_IO_file_xsgetn_mmap (_IO_FILE *fp, void *data, _IO_size_t n)
+{
+ _IO_size_t have;
+ char *read_ptr = fp->_IO_read_ptr;
+ char *s = (char *) data;
+
+ have = fp->_IO_read_end - fp->_IO_read_ptr;
+
+ if (have < n)
+ {
+ if (__glibc_unlikely (_IO_in_backup (fp)))
+ {
+#ifdef _LIBC
+ s = __mempcpy (s, read_ptr, have);
+#else
+ memcpy (s, read_ptr, have);
+ s += have;
+#endif
+ n -= have;
+ _IO_switch_to_main_get_area (fp);
+ read_ptr = fp->_IO_read_ptr;
+ have = fp->_IO_read_end - fp->_IO_read_ptr;
+ }
+
+ if (have < n)
+ {
+ /* Check that we are mapping all of the file, in case it grew. */
+ if (__glibc_unlikely (mmap_remap_check (fp)))
+ /* We punted mmap, so complete with the vanilla code. */
+ return s - (char *) data + _IO_XSGETN (fp, data, n);
+
+ read_ptr = fp->_IO_read_ptr;
+ have = fp->_IO_read_end - read_ptr;
+ }
+ }
+
+ if (have < n)
+ fp->_flags |= _IO_EOF_SEEN;
+
+ if (have != 0)
+ {
+ have = MIN (have, n);
+#ifdef _LIBC
+ s = __mempcpy (s, read_ptr, have);
+#else
+ memcpy (s, read_ptr, have);
+ s += have;
+#endif
+ fp->_IO_read_ptr = read_ptr + have;
+ }
+
+ return s - (char *) data;
+}
+
+static _IO_size_t
+_IO_file_xsgetn_maybe_mmap (_IO_FILE *fp, void *data, _IO_size_t n)
+{
+ /* We only get here if this is the first attempt to read something.
+ Decide which operations to use and then punt to the chosen one. */
+
+ decide_maybe_mmap (fp);
+ return _IO_XSGETN (fp, data, n);
+}
+
+#ifdef _LIBC
+versioned_symbol (libc, _IO_new_do_write, _IO_do_write, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_attach, _IO_file_attach, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_close_it, _IO_file_close_it, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_finish, _IO_file_finish, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_fopen, _IO_file_fopen, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_init, _IO_file_init, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_setbuf, _IO_file_setbuf, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_sync, _IO_file_sync, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_overflow, _IO_file_overflow, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_seekoff, _IO_file_seekoff, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_underflow, _IO_file_underflow, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_write, _IO_file_write, GLIBC_2_1);
+versioned_symbol (libc, _IO_new_file_xsputn, _IO_file_xsputn, GLIBC_2_1);
+#endif
+
+const struct _IO_jump_t _IO_file_jumps libio_vtable =
+{
+ JUMP_INIT_DUMMY,
+ JUMP_INIT(finish, _IO_file_finish),
+ JUMP_INIT(overflow, _IO_file_overflow),
+ JUMP_INIT(underflow, _IO_file_underflow),
+ JUMP_INIT(uflow, _IO_default_uflow),
+ JUMP_INIT(pbackfail, _IO_default_pbackfail),
+ JUMP_INIT(xsputn, _IO_file_xsputn),
+ JUMP_INIT(xsgetn, _IO_file_xsgetn),
+ JUMP_INIT(seekoff, _IO_new_file_seekoff),
+ JUMP_INIT(seekpos, _IO_default_seekpos),
+ JUMP_INIT(setbuf, _IO_new_file_setbuf),
+ JUMP_INIT(sync, _IO_new_file_sync),
+ JUMP_INIT(doallocate, _IO_file_doallocate),
+ JUMP_INIT(read, _IO_file_read),
+ JUMP_INIT(write, _IO_new_file_write),
+ JUMP_INIT(seek, _IO_file_seek),
+ JUMP_INIT(close, _IO_file_close),
+ JUMP_INIT(stat, _IO_file_stat),
+ JUMP_INIT(showmanyc, _IO_default_showmanyc),
+ JUMP_INIT(imbue, _IO_default_imbue)
+};
+libc_hidden_data_def (_IO_file_jumps)
+
+const struct _IO_jump_t _IO_file_jumps_mmap libio_vtable =
+{
+ JUMP_INIT_DUMMY,
+ JUMP_INIT(finish, _IO_file_finish),
+ JUMP_INIT(overflow, _IO_file_overflow),
+ JUMP_INIT(underflow, _IO_file_underflow_mmap),
+ JUMP_INIT(uflow, _IO_default_uflow),
+ JUMP_INIT(pbackfail, _IO_default_pbackfail),
+ JUMP_INIT(xsputn, _IO_new_file_xsputn),
+ JUMP_INIT(xsgetn, _IO_file_xsgetn_mmap),
+ JUMP_INIT(seekoff, _IO_file_seekoff_mmap),
+ JUMP_INIT(seekpos, _IO_default_seekpos),
+ JUMP_INIT(setbuf, (_IO_setbuf_t) _IO_file_setbuf_mmap),
+ JUMP_INIT(sync, _IO_file_sync_mmap),
+ JUMP_INIT(doallocate, _IO_file_doallocate),
+ JUMP_INIT(read, _IO_file_read),
+ JUMP_INIT(write, _IO_new_file_write),
+ JUMP_INIT(seek, _IO_file_seek),
+ JUMP_INIT(close, _IO_file_close_mmap),
+ JUMP_INIT(stat, _IO_file_stat),
+ JUMP_INIT(showmanyc, _IO_default_showmanyc),
+ JUMP_INIT(imbue, _IO_default_imbue)
+};
+
+const struct _IO_jump_t _IO_file_jumps_maybe_mmap libio_vtable =
+{
+ JUMP_INIT_DUMMY,
+ JUMP_INIT(finish, _IO_file_finish),
+ JUMP_INIT(overflow, _IO_file_overflow),
+ JUMP_INIT(underflow, _IO_file_underflow_maybe_mmap),
+ JUMP_INIT(uflow, _IO_default_uflow),
+ JUMP_INIT(pbackfail, _IO_default_pbackfail),
+ JUMP_INIT(xsputn, _IO_new_file_xsputn),
+ JUMP_INIT(xsgetn, _IO_file_xsgetn_maybe_mmap),
+ JUMP_INIT(seekoff, _IO_file_seekoff_maybe_mmap),
+ JUMP_INIT(seekpos, _IO_default_seekpos),
+ JUMP_INIT(setbuf, (_IO_setbuf_t) _IO_file_setbuf_mmap),
+ JUMP_INIT(sync, _IO_new_file_sync),
+ JUMP_INIT(doallocate, _IO_file_doallocate),
+ JUMP_INIT(read, _IO_file_read),
+ JUMP_INIT(write, _IO_new_file_write),
+ JUMP_INIT(seek, _IO_file_seek),
+ JUMP_INIT(close, _IO_file_close),
+ JUMP_INIT(stat, _IO_file_stat),
+ JUMP_INIT(showmanyc, _IO_default_showmanyc),
+ JUMP_INIT(imbue, _IO_default_imbue)
+};