Separate ftell from fseek logic and avoid modifying FILE data (#16532)

ftell semantics are distinct from fseek(SEEK_CUR) especially when it
is called on a file handler that is not yet active.  Due to this
caveat, much care needs to be taken while modifying the handler data
and hence, this first iteration on separating out ftell focusses on
maintaining handler data integrity at all times while it figures out
the current stream offset.  The result is that it makes a syscall for
every offset request.

There is scope for optimizing this by caching offsets when we know
that the handler is active.  A simple way to find out is when the
buffers have data.  It is not so simple to find this out when the
buffer is empty without adding some kind of flag.
This commit is contained in:
Siddhesh Poyarekar 2014-03-04 07:45:58 +05:30
parent 116f4d1ad6
commit 000232b9bc
7 changed files with 602 additions and 135 deletions

View File

@ -1,3 +1,15 @@
2014-03-04 Siddhesh Poyarekar <siddhesh@redhat.com>
[BZ #16532]
* libio/libioP.h (get_file_offset): New function.
* libio/fileops.c (get_file_offset): Likewise.
(do_ftell): Likewise.
(_IO_new_file_seekoff): Split out ftell logic.
* libio/wfileops.c (do_ftell_wide): Likewise.
(_IO_wfile_seekoff): Split out ftell logic.
* libio/tst-ftell-active-handler.c: New test case.
* libio/Makefile (tests): Add it.
2014-03-03 Roland McGrath <roland@hack.frob.com>
* nptl/ChangeLog: Renamed to nptl/ChangeLog.old, no longer used.

4
NEWS
View File

@ -9,8 +9,8 @@ Version 2.20
* The following bugs are resolved with this release:
15347, 15804, 15894, 16447, 16545, 16574, 16600, 16609, 16610, 16611,
16613, 16623, 16632.
15347, 15804, 15894, 16447, 16532, 16545, 16574, 16600, 16609, 16610,
16611, 16613, 16623, 16632.
* The am33 port, which had not worked for several years, has been removed
from ports.

View File

@ -60,7 +60,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \
tst-wmemstream1 tst-wmemstream2 \
bug-memstream1 bug-wmemstream1 \
tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
tst-fwrite-error tst-ftell-partial-wide
tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler
ifeq (yes,$(build-shared))
# Add test-fopenloc only if shared library is enabled since it depends on
# shared localedata objects.

View File

@ -929,6 +929,71 @@ _IO_file_sync_mmap (_IO_FILE *fp)
return 0;
}
/* Get the current file offset using a system call. This is the safest method
to get the current file offset, since we are sure that we get the current
state of the file. Before the stream handle is activated (by using fread,
fwrite, etc.), an application may alter the state of the file descriptor
underlying it by calling read/write/lseek on it. Using a cached offset at
this point will result in returning the incorrect value. Same is the case
when one switches from reading in a+ mode to writing, where the buffer has
not been flushed - the cached offset would reflect the reading position
while the actual write position would be at the end of the file.
do_ftell and do_ftell_wide may resort to using the cached offset in some
special cases instead of calling get_file_offset, but those cases should be
thoroughly described. */
_IO_off64_t
get_file_offset (_IO_FILE *fp)
{
if ((fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING)
{
struct stat64 st;
bool ret = (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode));
if (ret)
return st.st_size;
else
return EOF;
}
else
return _IO_SYSSEEK (fp, 0, _IO_seek_cur);
}
/* ftell{,o} implementation. Don't modify any state of the file pointer while
we try to get the current state of the stream. */
static _IO_off64_t
do_ftell (_IO_FILE *fp)
{
_IO_off64_t result;
result = get_file_offset (fp);
if (result == EOF)
return result;
/* No point looking at unflushed data if we haven't allocated buffers
yet. */
if (fp->_IO_buf_base != NULL)
{
bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
|| _IO_in_put_mode (fp));
/* Adjust for unflushed data. */
if (!was_writing)
result -= fp->_IO_read_end - fp->_IO_read_ptr;
else
result += fp->_IO_write_ptr - fp->_IO_read_end;
}
if (result < 0)
{
__set_errno (EINVAL);
return EOF;
}
return result;
}
_IO_off64_t
_IO_new_file_seekoff (fp, offset, dir, mode)
@ -940,6 +1005,13 @@ _IO_new_file_seekoff (fp, offset, dir, 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
@ -948,9 +1020,6 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
|| _IO_in_put_mode (fp));
if (mode == 0)
dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
/* 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
@ -958,7 +1027,7 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
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. */
else if (was_writing && _IO_switch_to_get_mode (fp))
if (was_writing && _IO_switch_to_get_mode (fp))
return EOF;
if (fp->_IO_buf_base == NULL)
@ -978,30 +1047,10 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
{
case _IO_seek_cur:
/* Adjust for read-ahead (bytes is buffer). */
if (mode != 0 || !was_writing)
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
else
{
/* _IO_read_end coincides with fp._offset, so the actual file position
is fp._offset - (_IO_read_end - new_write_ptr). This is fine
even if fp._offset is not set, since fp->_IO_read_end is then at
_IO_buf_base and this adjustment is for unbuffered output. */
offset -= fp->_IO_read_end - fp->_IO_write_ptr;
}
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
if (fp->_offset == _IO_pos_BAD)
{
if (mode != 0)
goto dumb;
else
{
result = _IO_SYSSEEK (fp, 0, dir);
if (result == EOF)
return result;
fp->_offset = result;
}
}
goto dumb;
/* Make offset absolute, assuming current pointer is file_ptr(). */
offset += fp->_offset;
if (offset < 0)
@ -1028,10 +1077,6 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
}
/* At this point, dir==_IO_seek_set. */
/* If we are only interested in the current position we've found it now. */
if (mode == 0)
return offset;
/* If destination is within current buffer, optimize: */
if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
&& !_IO_in_backup (fp))

View File

@ -397,6 +397,7 @@ extern void _IO_wdoallocbuf (_IO_FILE *) __THROW;
libc_hidden_proto (_IO_wdoallocbuf)
extern void _IO_unsave_wmarkers (_IO_FILE *) __THROW;
extern unsigned _IO_adjust_wcolumn (unsigned, const wchar_t *, int) __THROW;
extern _IO_off64_t get_file_offset (_IO_FILE *fp);
/* Marker-related function. */

View File

@ -0,0 +1,384 @@
/* Verify that ftell returns the correct value at various points before and
after the handler on which it is called becomes active.
Copyright (C) 2014 Free Software Foundation, Inc.
This file is part of the GNU C Library.
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/>. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <locale.h>
#include <wchar.h>
static int do_test (void);
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"
#define get_handles_fdopen(filename, fd, fp, fd_mode, mode) \
({ \
int ret = 0; \
(fd) = open ((filename), (fd_mode), 0); \
if ((fd) == -1) \
{ \
printf ("open failed: %m\n"); \
ret = 1; \
} \
else \
{ \
(fp) = fdopen ((fd), (mode)); \
if ((fp) == NULL) \
{ \
printf ("fdopen failed: %m\n"); \
close (fd); \
ret = 1; \
} \
} \
ret; \
})
#define get_handles_fopen(filename, fd, fp, mode) \
({ \
int ret = 0; \
(fp) = fopen ((filename), (mode)); \
if ((fp) == NULL) \
{ \
printf ("fopen failed: %m\n"); \
ret = 1; \
} \
else \
{ \
(fd) = fileno (fp); \
if ((fd) == -1) \
{ \
printf ("fileno failed: %m\n"); \
ret = 1; \
} \
} \
ret; \
})
/* data points to either char_data or wide_data, depending on whether we're
testing regular file mode or wide mode respectively. Similarly,
fputs_func points to either fputs or fputws. data_len keeps track of the
length of the current data and file_len maintains the current file
length. */
static const void *data;
static const char *char_data = "abcdef";
static const wchar_t *wide_data = L"abcdef";
static size_t data_len;
static size_t file_len;
typedef int (*fputs_func_t) (const void *data, FILE *fp);
fputs_func_t fputs_func;
/* Test that the value of ftell is not cached when the stream handle is not
active. */
static int
do_ftell_test (const char *filename)
{
int ret = 0;
struct test
{
const char *mode;
int fd_mode;
size_t old_off;
size_t new_off;
} test_modes[] = {
/* In w, w+ and r+ modes, the file position should be at the
beginning of the file. After the write, the offset should be
updated to data_len. */
{"w", O_WRONLY, 0, data_len},
{"w+", O_RDWR, 0, data_len},
{"r+", O_RDWR, 0, data_len},
/* For 'a' and 'a+' modes, the initial file position should be the
current end of file. After the write, the offset has data_len
added to the old value. */
{"a", O_WRONLY, data_len, 2 * data_len},
{"a+", O_RDWR, 2 * data_len, 3 * data_len},
};
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
{
FILE *fp;
int fd;
printf ("\tftell: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen",
test_modes[i].mode);
if (j == 0)
ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
test_modes[i].mode);
else
ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
if (ret != 0)
return ret;
long off = ftell (fp);
if (off != test_modes[i].old_off)
{
printf ("Incorrect old offset. Expected %zu but got %ld, ",
test_modes[i].old_off, off);
ret |= 1;
}
else
printf ("old offset = %ld, ", off);
/* The effect of this write on the offset should be seen in the ftell
call that follows it. */
int ret = write (fd, data, data_len);
off = ftell (fp);
if (off != test_modes[i].new_off)
{
printf ("Incorrect new offset. Expected %zu but got %ld\n",
test_modes[i].old_off, off);
ret |= 1;
}
else
printf ("new offset = %ld\n", off);
fclose (fp);
}
}
return ret;
}
/* This test opens the file for writing, moves the file offset of the
underlying file, writes out data and then checks if ftell trips on it. */
static int
do_write_test (const char *filename)
{
FILE *fp = NULL;
int fd;
int ret = 0;
struct test
{
const char *mode;
int fd_mode;
} test_modes[] = {
{"w", O_WRONLY},
{"w+", O_RDWR},
{"r+", O_RDWR}
};
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
{
printf ("\twrite: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
test_modes[i].mode);
if (j == 0)
ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
else
ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
test_modes[i].mode);
if (ret != 0)
return ret;
/* Move offset to just before the end of the file. */
off_t ret = lseek (fd, file_len - 1, SEEK_SET);
if (ret == -1)
{
printf ("lseek failed: %m\n");
ret |= 1;
}
/* Write some data. */
size_t written = fputs_func (data, fp);
if (written == EOF)
{
printf ("fputs[1] failed to write data\n");
ret |= 1;
}
/* Verify that the offset points to the end of the file. The length
of the file would be the original length + the length of data
written to it - the amount by which we moved the offset using
lseek. */
long offset = ftell (fp);
file_len = file_len - 1 + data_len;
if (offset != file_len)
{
printf ("Incorrect offset. Expected %zu, but got %ld\n",
file_len, offset);
ret |= 1;
}
printf ("offset = %ld\n", offset);
fclose (fp);
}
}
return ret;
}
/* This test opens a file in append mode, writes some data, and then verifies
that ftell does not trip over it. */
static int
do_append_test (const char *filename)
{
FILE *fp = NULL;
int ret = 0;
int fd;
struct test
{
const char *mode;
int fd_mode;
} test_modes[] = {
{"a", O_WRONLY},
{"a+", O_RDWR}
};
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
{
printf ("\tappend: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
test_modes[i].mode);
if (j == 0)
ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
else
ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
test_modes[i].mode);
if (ret != 0)
return ret;
/* Write some data. */
size_t written = fputs_func (data, fp);
if (written == EOF)
{
printf ("fputs[1] failed to write all data\n");
ret |= 1;
}
/* Verify that the offset points to the end of the file. The file
len is maintained by adding data_len each time to reflect the data
written to it. */
long offset = ftell (fp);
file_len += data_len;
if (offset != file_len)
{
printf ("Incorrect offset. Expected %zu, but got %ld\n",
file_len, offset);
ret |= 1;
}
printf ("offset = %ld\n", offset);
fclose (fp);
}
}
return ret;
}
static int
do_one_test (const char *filename)
{
int ret = 0;
ret |= do_ftell_test (filename);
ret |= do_write_test (filename);
ret |= do_append_test (filename);
return ret;
}
/* Run a set of tests for ftell for regular files and wide mode files. */
static int
do_test (void)
{
int ret = 0;
FILE *fp = NULL;
char *filename;
size_t written;
int fd = create_temp_file ("tst-active-handler-tmp.", &filename);
if (fd == -1)
{
printf ("create_temp_file: %m\n");
return 1;
}
fp = fdopen (fd, "w");
if (fp == NULL)
{
printf ("fdopen[0]: %m\n");
close (fd);
return 1;
}
data = char_data;
data_len = strlen (char_data);
file_len = strlen (char_data);
written = fputs (data, fp);
if (written == EOF)
{
printf ("fputs[1] failed to write data\n");
ret = 1;
}
fclose (fp);
if (ret)
return ret;
/* Tests for regular files. */
puts ("Regular mode:");
fputs_func = (fputs_func_t) fputs;
data = char_data;
data_len = strlen (char_data);
ret |= do_one_test (filename);
/* Truncate the file before repeating the tests in wide mode. */
fp = fopen (filename, "w");
if (fp == NULL)
{
printf ("fopen failed %m\n");
return 1;
}
fclose (fp);
/* Tests for wide files. */
puts ("Wide mode:");
if (setlocale (LC_ALL, "en_US.UTF-8") == NULL)
{
printf ("Cannot set en_US.UTF-8 locale.\n");
return 1;
}
fputs_func = (fputs_func_t) fputws;
data = wide_data;
data_len = wcslen (wide_data);
ret |= do_one_test (filename);
return ret;
}

View File

@ -596,29 +596,24 @@ done:
return 0;
}
_IO_off64_t
_IO_wfile_seekoff (fp, offset, dir, mode)
_IO_FILE *fp;
_IO_off64_t offset;
int dir;
int mode;
/* ftell{,o} implementation for wide mode. Don't modify any state of the file
pointer while we try to get the current state of the stream. */
static _IO_off64_t
do_ftell_wide (_IO_FILE *fp)
{
_IO_off64_t result;
_IO_off64_t delta, new_offset;
long int count;
/* 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->_wide_data->_IO_read_base
== fp->_wide_data->_IO_read_end)
&& (fp->_wide_data->_IO_write_base
== fp->_wide_data->_IO_write_ptr));
_IO_off64_t result, offset = 0;
bool was_writing = ((fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base)
|| _IO_in_put_mode (fp));
if (mode == 0)
/* No point looking for offsets in the buffer if it hasn't even been
allocated. */
if (fp->_wide_data->_IO_buf_base != NULL)
{
const wchar_t *wide_read_base;
const wchar_t *wide_read_ptr;
const wchar_t *wide_read_end;
bool was_writing = ((fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base)
|| _IO_in_put_mode (fp));
/* XXX For wide stream with backup store it is not very
reasonable to determine the offset. The pushed-back
character might require a state change and we need not be
@ -633,14 +628,120 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
return -1;
}
/* There is no more data in the backup buffer. We can
switch back. */
_IO_switch_to_main_wget_area (fp);
/* Nothing in the backup store, so note the backed up pointers
without changing the state. */
wide_read_base = fp->_wide_data->_IO_save_base;
wide_read_ptr = wide_read_base;
wide_read_end = fp->_wide_data->_IO_save_end;
}
else
{
wide_read_base = fp->_wide_data->_IO_read_base;
wide_read_ptr = fp->_wide_data->_IO_read_ptr;
wide_read_end = fp->_wide_data->_IO_read_end;
}
dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
struct _IO_codecvt *cv = fp->_codecvt;
int clen = (*cv->__codecvt_do_encoding) (cv);
if (!was_writing)
{
if (clen > 0)
{
offset -= (wide_read_end - wide_read_ptr) * clen;
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
}
else
{
int nread;
size_t delta = wide_read_ptr - wide_read_base;
__mbstate_t state = fp->_wide_data->_IO_last_state;
nread = (*cv->__codecvt_do_length) (cv, &state,
fp->_IO_read_base,
fp->_IO_read_end, delta);
offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
}
}
else
{
if (clen > 0)
offset += (fp->_wide_data->_IO_write_ptr
- fp->_wide_data->_IO_write_base) * clen;
else
{
size_t delta = (fp->_wide_data->_IO_write_ptr
- fp->_wide_data->_IO_write_base);
/* Allocate enough space for the conversion. */
size_t outsize = delta * sizeof (wchar_t);
char *out = malloc (outsize);
char *outstop = out;
const wchar_t *in = fp->_wide_data->_IO_write_base;
enum __codecvt_result status;
__mbstate_t state = fp->_wide_data->_IO_last_state;
status = (*cv->__codecvt_do_out) (cv, &state,
in, in + delta, &in,
out, out + outsize, &outstop);
/* We don't check for __codecvt_partial because it can be
returned on one of two conditions: either the output
buffer is full or the input sequence is incomplete. We
take care to allocate enough buffer and our input
sequences must be complete since they are accepted as
wchar_t; if not, then that is an error. */
if (__glibc_unlikely (status != __codecvt_ok))
return WEOF;
offset += outstop - out;
}
/* _IO_read_end coincides with fp._offset, so the actual file position
is fp._offset - (_IO_read_end - new_write_ptr). */
offset -= fp->_IO_read_end - fp->_IO_write_ptr;
}
}
result = get_file_offset (fp);
if (result == EOF)
return result;
result += offset;
return result;
}
_IO_off64_t
_IO_wfile_seekoff (fp, offset, dir, mode)
_IO_FILE *fp;
_IO_off64_t offset;
int dir;
int mode;
{
_IO_off64_t result;
_IO_off64_t delta, new_offset;
long int 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_wide (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->_wide_data->_IO_read_base
== fp->_wide_data->_IO_read_end)
&& (fp->_wide_data->_IO_write_base
== fp->_wide_data->_IO_write_ptr));
bool was_writing = ((fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_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
@ -648,7 +749,7 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
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. */
else if (was_writing && _IO_switch_to_wget_mode (fp))
if (was_writing && _IO_switch_to_wget_mode (fp))
return WEOF;
if (fp->_wide_data->_IO_buf_base == NULL)
@ -693,7 +794,6 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
{
int nread;
flushed:
delta = (fp->_wide_data->_IO_read_ptr
- fp->_wide_data->_IO_read_base);
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
@ -706,80 +806,9 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
}
}
else
{
char *new_write_ptr = fp->_IO_write_ptr;
if (clen > 0)
offset += (fp->_wide_data->_IO_write_ptr
- fp->_wide_data->_IO_write_base) / clen;
else
{
enum __codecvt_result status = __codecvt_ok;
delta = (fp->_wide_data->_IO_write_ptr
- fp->_wide_data->_IO_write_base);
const wchar_t *write_base = fp->_wide_data->_IO_write_base;
/* FIXME: This actually ends up in two iterations of conversion,
one here and the next when the buffer actually gets flushed.
It may be possible to optimize this in future so that
wdo_write identifies already converted content and does not
redo it. In any case, this is much better than having to
flush buffers for every ftell. */
do
{
/* There is not enough space in the buffer to do the entire
conversion, so there is no point trying to avoid the
buffer flush. Just do it and go back to how it was with
the read mode. */
if (status == __codecvt_partial
|| (delta > 0 && new_write_ptr == fp->_IO_buf_end))
{
if (_IO_switch_to_wget_mode (fp))
return WEOF;
goto flushed;
}
const wchar_t *new_wbase = fp->_wide_data->_IO_write_base;
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
status = (*cv->__codecvt_do_out) (cv,
&fp->_wide_data->_IO_state,
write_base,
write_base + delta,
&new_wbase,
new_write_ptr,
fp->_IO_buf_end,
&new_write_ptr);
delta -= new_wbase - write_base;
/* If there was an error, then return WEOF.
TODO: set buffer state. */
if (__glibc_unlikely (status == __codecvt_error))
return WEOF;
}
while (delta > 0);
}
/* _IO_read_end coincides with fp._offset, so the actual file position
is fp._offset - (_IO_read_end - new_write_ptr). This is fine
even if fp._offset is not set, since fp->_IO_read_end is then at
_IO_buf_base and this adjustment is for unbuffered output. */
offset -= fp->_IO_read_end - new_write_ptr;
}
if (fp->_offset == _IO_pos_BAD)
{
if (mode != 0)
goto dumb;
else
{
result = _IO_SYSSEEK (fp, 0, dir);
if (result == EOF)
return result;
fp->_offset = result;
}
}
goto dumb;
/* Make offset absolute, assuming current pointer is file_ptr(). */
offset += fp->_offset;
@ -802,10 +831,6 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
}
/* At this point, dir==_IO_seek_set. */
/* If we are only interested in the current position we've found it now. */
if (mode == 0)
return offset;
/* If destination is within current buffer, optimize: */
if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
&& !_IO_in_backup (fp))