mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
Don't flush write buffer for ftell
[BZ #5298] Use write pointer state along with the file offset and/or the read pointers to get the current file position.
This commit is contained in:
116
libio/wfileops.c
116
libio/wfileops.c
@ -613,6 +613,10 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
|
||||
&& (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));
|
||||
|
||||
if (mode == 0)
|
||||
{
|
||||
/* XXX For wide stream with backup store it is not very
|
||||
@ -644,11 +648,8 @@ _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. */
|
||||
|
||||
if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
|
||||
|| _IO_in_put_mode (fp))
|
||||
if (_IO_switch_to_wget_mode (fp))
|
||||
return WEOF;
|
||||
else if (was_writing && _IO_switch_to_wget_mode (fp))
|
||||
return WEOF;
|
||||
|
||||
if (fp->_wide_data->_IO_buf_base == NULL)
|
||||
{
|
||||
@ -679,29 +680,104 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
|
||||
cv = fp->_codecvt;
|
||||
clen = (*cv->__codecvt_do_encoding) (cv);
|
||||
|
||||
if (clen > 0)
|
||||
if (mode != 0 || !was_writing)
|
||||
{
|
||||
offset -= (fp->_wide_data->_IO_read_end
|
||||
- fp->_wide_data->_IO_read_ptr) * clen;
|
||||
/* Adjust by readahead in external buffer. */
|
||||
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
|
||||
if (clen > 0)
|
||||
{
|
||||
offset -= (fp->_wide_data->_IO_read_end
|
||||
- fp->_wide_data->_IO_read_ptr) * clen;
|
||||
/* Adjust by readahead in external buffer. */
|
||||
offset -= fp->_IO_read_end - fp->_IO_read_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
nread = (*cv->__codecvt_do_length) (cv,
|
||||
&fp->_wide_data->_IO_state,
|
||||
fp->_IO_read_base,
|
||||
fp->_IO_read_end, delta);
|
||||
fp->_IO_read_ptr = fp->_IO_read_base + nread;
|
||||
fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
|
||||
offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int nread;
|
||||
char *new_write_ptr = fp->_IO_write_ptr;
|
||||
|
||||
delta = fp->_wide_data->_IO_read_ptr - fp->_wide_data->_IO_read_base;
|
||||
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
|
||||
nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
|
||||
fp->_IO_read_base,
|
||||
fp->_IO_read_end, delta);
|
||||
fp->_IO_read_ptr = fp->_IO_read_base + nread;
|
||||
fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
|
||||
offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
|
||||
if (clen > 0)
|
||||
offset += (fp->_wide_data->_IO_write_ptr
|
||||
- fp->_wide_data->_IO_write_base) / clen;
|
||||
else
|
||||
{
|
||||
enum __codecvt_result status;
|
||||
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
|
||||
{
|
||||
/* Ugh, no point trying to avoid the flush. Just do it
|
||||
and go back to how it was with the read mode. */
|
||||
if (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 (__builtin_expect (status == __codecvt_error, 0))
|
||||
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)
|
||||
goto dumb;
|
||||
{
|
||||
if (mode != 0)
|
||||
goto dumb;
|
||||
else
|
||||
{
|
||||
result = _IO_SYSSEEK (fp, 0, dir);
|
||||
if (result == EOF)
|
||||
return result;
|
||||
fp->_offset = result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make offset absolute, assuming current pointer is file_ptr(). */
|
||||
offset += fp->_offset;
|
||||
|
||||
|
Reference in New Issue
Block a user