summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--NEWS13
-rw-r--r--libio/fileops.c21
-rw-r--r--libio/wfileops.c116
4 files changed, 126 insertions, 31 deletions
diff --git a/ChangeLog b/ChangeLog
index 255e0751cf..7010a8e757 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
2012-09-28 Siddhesh Poyarekar <siddhesh@redhat.com>
+ [BZ #5298]
+ * libio/fileops.c (_IO_new_file_seekoff): Don't flush buffer
+ for ftell. Compute offsets from write pointers instead.
+ * libio/wfileops.c (_IO_wfile_seekoff): Likewise.
+
+2012-09-28 Siddhesh Poyarekar <siddhesh@redhat.com>
+
[BZ #14543]
* libio/Makefile (tests): New test case tst-fseek.
* libio/tst-fseek.c: New test case to verify that fseek/ftell
diff --git a/NEWS b/NEWS
index 70ddcbe93c..b8a01efe9b 100644
--- a/NEWS
+++ b/NEWS
@@ -9,12 +9,13 @@ Version 2.17
* The following bugs are resolved with this release:
- 1349, 3479, 5044, 5400, 6778, 6808, 9685, 9914, 10014, 10038, 11607,
- 13412, 13542, 13629, 13679, 13696, 13717, 13741, 13939, 13966, 14042,
- 14090, 14150, 14151, 14154, 14157, 14166, 14173, 14195, 14237, 14252,
- 14283, 14298, 14303, 14307, 14328, 14331, 14336, 14337, 14347, 14349,
- 14376, 14459, 14476, 14505, 14510, 14516, 14518, 14519, 14530, 14532,
- 14538, 14543, 14544, 14545, 14562, 14576, 14579, 14583, 14587, 14621.
+ 1349, 3479, 5044, 5298, 5400, 6778, 6808, 9685, 9914, 10014, 10038,
+ 11607, 13412, 13542, 13629, 13679, 13696, 13717, 13741, 13939, 13966,
+ 14042, 14090, 14150, 14151, 14154, 14157, 14166, 14173, 14195, 14237,
+ 14252, 14283, 14298, 14303, 14307, 14328, 14331, 14336, 14337, 14347,
+ 14349, 14376, 14459, 14476, 14505, 14510, 14516, 14518, 14519, 14530,
+ 14532, 14538, 14543, 14544, 14545, 14562, 14576, 14579, 14583, 14587,
+ 14621.
* Support for STT_GNU_IFUNC symbols added for s390 and s390x.
Optimized versions of memcpy, memset, and memcmp added for System z10 and
diff --git a/libio/fileops.c b/libio/fileops.c
index e22efdec13..173091cc6c 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -978,6 +978,9 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
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));
+
if (mode == 0)
dir = _IO_seek_cur, offset = 0; /* Don't move any pointers. */
@@ -988,10 +991,8 @@ _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. */
-
- if (fp->_IO_write_ptr > fp->_IO_write_base || _IO_in_put_mode (fp))
- if (_IO_switch_to_get_mode (fp))
- return EOF;
+ else if (was_writing && _IO_switch_to_get_mode (fp))
+ return EOF;
if (fp->_IO_buf_base == NULL)
{
@@ -1010,7 +1011,17 @@ _IO_new_file_seekoff (fp, offset, dir, mode)
{
case _IO_seek_cur:
/* Adjust for read-ahead (bytes is buffer). */
- offset -= fp->_IO_read_end - fp->_IO_read_ptr;
+ 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;
+ }
+
if (fp->_offset == _IO_pos_BAD)
{
if (mode != 0)
diff --git a/libio/wfileops.c b/libio/wfileops.c
index d3f46b2ed1..1087e8df8d 100644
--- a/libio/wfileops.c
+++ b/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;