Blob Blame History Raw
diff -Nrup a/libio/fileops.c b/libio/fileops.c
--- a/libio/fileops.c	2012-06-30 15:12:34.000000000 -0400
+++ b/libio/fileops.c	2012-11-28 12:27:00.297009479 -0500
@@ -978,6 +978,9 @@ _IO_new_file_seekoff (fp, offset, dir, m
   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, m
      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-papped 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, m
     {
     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 -Nrup a/libio/wfileops.c b/libio/wfileops.c
--- a/libio/wfileops.c	2012-11-28 10:47:08.906188748 -0500
+++ b/libio/wfileops.c	2012-11-28 12:34:06.285439028 -0500
@@ -611,6 +611,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
@@ -642,11 +646,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)
     {
@@ -677,29 +678,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;
 
Binary files a/libio/.wfileops.c.swp and b/libio/.wfileops.c.swp differ