about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--libio/Makefile2
-rw-r--r--libio/bug-wsetpos.c75
-rw-r--r--libio/wfileops.c49
4 files changed, 97 insertions, 36 deletions
diff --git a/ChangeLog b/ChangeLog
index ef087c7944..23cf69eae4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2009-09-02  Andreas Schwab  <schwab@redhat.com>
+
+	* libio/wfileops.c (_IO_wfile_seekoff): Account for readahead in
+	external buffer.  Always discard readahead in internal buffer.
+	* libio/Makefile (tests): Add bug-wsetpos.
+	* libio/bug-wsetpos.c: New file.
+
 2009-09-02  Jakub Jelinek  <jakub@redhat.com>
 
 	* sysdeps/x86_64/multiarch/strstr-c.c (__strstr_sse42, __strstr_sse2):
diff --git a/libio/Makefile b/libio/Makefile
index 501e80c2ee..83b9458dc2 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -58,7 +58,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst-memstream1 tst-memstream2 \
 	tst-wmemstream1 tst-wmemstream2 \
 	bug-memstream1 bug-wmemstream1 \
-	tst-setvbuf1 tst-popen1 tst-fgetwc
+	tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos
 test-srcs = test-freopen
 
 all: # Make this the default target; it will be defined in Rules.
diff --git a/libio/bug-wsetpos.c b/libio/bug-wsetpos.c
new file mode 100644
index 0000000000..ccb22a4b62
--- /dev/null
+++ b/libio/bug-wsetpos.c
@@ -0,0 +1,75 @@
+/* Test program for fsetpos on a wide character stream.  */
+
+#include <assert.h>
+#include <stdio.h>
+#include <wchar.h>
+
+static void do_prepare (void);
+#define PREPARE(argc, argv) do_prepare ()
+static int do_test (void);
+#define TEST_FUNCTION do_test ()
+#include <test-skeleton.c>
+
+static const char pattern[] = "12345";
+static char *temp_file;
+
+static void
+do_prepare (void)
+{
+  int fd = create_temp_file ("bug-wsetpos.", &temp_file);
+  if (fd == -1)
+    {
+      printf ("cannot create temporary file: %m\n");
+      exit (1);
+    }
+  write (fd, pattern, sizeof (pattern));
+  close (fd);
+}
+
+static int
+do_test (void)
+{
+  FILE *fp = fopen (temp_file, "r");
+  fpos_t pos;
+  wchar_t c;
+
+  if (fp == NULL)
+    {
+      printf ("fdopen: %m\n");
+      return 1;
+    }
+
+  c = fgetwc (fp); assert (c == L'1');
+  c = fgetwc (fp); assert (c == L'2');
+
+  if (fgetpos (fp, &pos) == EOF)
+    {
+      printf ("fgetpos: %m\n");
+      return 1;
+    }
+
+  rewind (fp);
+  if (ferror (fp))
+    {
+      printf ("rewind: %m\n");
+      return 1;
+    }
+
+  c = fgetwc (fp); assert (c == L'1');
+
+  if (fsetpos (fp, &pos) == EOF)
+    {
+      printf ("fsetpos: %m\n");
+      return 1;
+    }
+
+  c = fgetwc (fp);
+  if (c != L'3')
+    {
+      puts ("fsetpos failed");
+      return 1;
+    }
+
+  puts ("Test succeeded.");
+  return 0;
+}
diff --git a/libio/wfileops.c b/libio/wfileops.c
index be8ae78bfb..5bc08bedfb 100644
--- a/libio/wfileops.c
+++ b/libio/wfileops.c
@@ -631,8 +631,12 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
       clen = (*cv->__codecvt_do_encoding) (cv);
 
       if (clen > 0)
-	offset -= (fp->_wide_data->_IO_read_end
-		   - fp->_wide_data->_IO_read_ptr) * clen;
+	{
+	  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;
@@ -690,39 +694,11 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
 		    fp->_IO_buf_base + (offset - start_offset),
 		    fp->_IO_read_end);
 	  _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
-
-	  /* Now set the pointer for the internal buffer.  This
-	     might be an iterative process.  Though the read
-	     pointer is somewhere in the current external buffer
-	     this does not mean we can convert this whole buffer
-	     at once fitting in the internal buffer.  */
-	  fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
-	  read_ptr_copy = fp->_IO_read_base;
-	  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_base;
-	  do
-	    {
-	      wchar_t buffer[1024];
-	      wchar_t *ignore;
-	      status = (*cd->__codecvt_do_in) (cd,
-					       &fp->_wide_data->_IO_state,
-					       read_ptr_copy,
-					       fp->_IO_read_ptr,
-					       &read_ptr_copy,
-					       buffer,
-					       buffer
-					       + (sizeof (buffer)
-						  / sizeof (buffer[0])),
-					       &ignore);
-	      if (status != __codecvt_ok && status != __codecvt_partial)
-		{
-		  fp->_flags |= _IO_ERR_SEEN;
-		  goto dumb;
-		}
-	    }
-	  while (read_ptr_copy != fp->_IO_read_ptr);
-
-	  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_base;
-
+	  _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
+		     fp->_wide_data->_IO_buf_base,
+		     fp->_wide_data->_IO_buf_base);
+	  _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
+		     fp->_wide_data->_IO_buf_base);
 	  _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
 	  goto resync;
 	}
@@ -760,6 +736,9 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
   _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);
+  _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
+	     fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
+  _IO_wsetp (fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
   fp->_offset = result + count;
   _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
   return offset;