]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Adjust wide data buffer pointers during fseek and ftell
authorSiddhesh Poyarekar <siddhesh@redhat.com>
Fri, 28 Sep 2012 12:50:40 +0000 (18:20 +0530)
committerSiddhesh Poyarekar <siddhesh@redhat.com>
Fri, 28 Sep 2012 12:51:39 +0000 (18:21 +0530)
[BZ #14543]
Set the internal buffer state correctly whenever the external buffer
state is modified by fseek by either computing the current
_IO_read_ptr/end for the internal buffer based on the new _IO_read_ptr
in the external buffer or converting the content read into the
external buffer, up to the extent of the requested fseek offset.

ChangeLog
NEWS
libio/Makefile
libio/tst-fseek.c [new file with mode: 0644]
libio/wfileops.c

index 3004989b6519c3d81cada3ffe4cab661291916c0..255e0751cf661ecbc3d362f2d41720f393758b06 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+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
+       combination works in wide mode.
+       * libio/wfileops.c (_IO_wfile_seekoff): Adjust internal buffer
+       state when the external buffer state changes.
+
 2012-09-27  David S. Miller  <davem@davemloft.net>
 
        [BZ #14376]
diff --git a/NEWS b/NEWS
index c0a671de21ca084ef525c1dd5ac895c6c13c13c7..70ddcbe93cb45b02ba6ef5e4c245ef3e8ea5c0b7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -14,7 +14,7 @@ Version 2.17
   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, 14544, 14545, 14562, 14576, 14579, 14583, 14587, 14621.
+  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
index e2a7efdc1c7e7726a207daea69d8b431dd53c89f..0d28cea59c29c3fed120bd9b55f266eb10dbd2b4 100644 (file)
@@ -59,7 +59,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 bug-wsetpos bug-fclose1
+       tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos bug-fclose1 tst-fseek
 ifeq (yes,$(build-shared))
 # Add test-fopenloc only if shared library is enabled since it depends on
 # shared localedata objects.
@@ -158,6 +158,7 @@ bug-ungetwc2-ENV = LOCPATH=$(common-objpfx)localedata
 tst-swscanf-ENV = LOCPATH=$(common-objpfx)localedata
 bug-ftell-ENV = LOCPATH=$(common-objpfx)localedata
 tst-fgetwc-ENV = LOCPATH=$(common-objpfx)localedata
+tst-fseek-ENV = LOCPATH=$(common-objpfx)localedata
 
 generated = tst-fopenloc.mtrace tst-fopenloc.check
 
diff --git a/libio/tst-fseek.c b/libio/tst-fseek.c
new file mode 100644 (file)
index 0000000..457a087
--- /dev/null
@@ -0,0 +1,173 @@
+/* Verify that fseek/ftell combination works for wide chars.
+   Copyright (C) 2012 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 <locale.h>
+#include <errno.h>
+#include <wchar.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Defined in test-skeleton.c.  */
+static int create_temp_file (const char *base, char **filename);
+
+
+static int
+do_seek_end (FILE *fp)
+{
+  long save;
+
+  if (fputws (L"abc\n", fp) == -1)
+    {
+      printf ("do_seek_end: fputws: %s\n", strerror (errno));
+      return 1;
+    }
+
+  save = ftell (fp);
+  rewind (fp);
+
+  if (fseek (fp, 0, SEEK_END) == -1)
+    {
+      printf ("do_seek_end: fseek: %s\n", strerror (errno));
+      return 1;
+    }
+
+  if (save != ftell (fp))
+    {
+      printf ("save = %ld, ftell = %ld\n", save, ftell (fp));
+      return 1;
+    }
+
+  return 0;
+}
+
+int
+do_seek_set (FILE *fp)
+{
+  long save1, save2;
+
+  if (fputws (L"ゅう\n", fp) == -1)
+    {
+      printf ("seek_set: fputws(1): %s\n", strerror (errno));
+      return 1;
+    }
+
+  save1 = ftell (fp);
+
+  if (fputws (L"ゅう\n", fp) == -1)
+    {
+      printf ("seek_set: fputws(2): %s\n", strerror (errno));
+      return 1;
+    }
+
+  save2 = ftell (fp);
+
+  if (fputws (L"ゅう\n", fp) == -1)
+    {
+      printf ("seek_set: fputws(3): %s\n", strerror (errno));
+      return 1;
+    }
+
+  if (fseek (fp, save1, SEEK_SET) == -1)
+    {
+      printf ("seek_set: fseek(1): %s\n", strerror (errno));
+      return 1;
+    }
+
+  if (save1 != ftell (fp))
+    {
+      printf ("save1 = %ld, ftell = %ld\n", save1, ftell (fp));
+      return 1;
+    }
+
+  if (fseek (fp, save2, SEEK_SET) == -1)
+    {
+      printf ("seek_set: fseek(2): %s\n", strerror (errno));
+      return 1;
+    }
+
+  if (save2 != ftell (fp))
+    {
+      printf ("save2 = %ld, ftell = %ld\n", save2, ftell (fp));
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  if (setlocale (LC_ALL, "ja_JP.UTF-8") == NULL)
+    {
+      printf ("Cannot set ja_JP.UTF-8 locale.\n");
+      exit (1);
+    }
+
+  /* Retain messages in English.  */
+  if (setlocale (LC_MESSAGES, "en_US.ISO-8859-1") == NULL)
+    {
+      printf ("Cannot set LC_MESSAGES to en_US.ISO-8859-1 locale.\n");
+      exit (1);
+    }
+
+  int ret = 0;
+  char *filename;
+  int fd = create_temp_file ("tst-fseek.out", &filename);
+
+  if (fd == -1)
+    return 1;
+
+  FILE *fp = fdopen (fd, "w+");
+  if (fp == NULL)
+    {
+      printf ("seek_set: fopen: %s\n", strerror (errno));
+      close (fd);
+      return 1;
+    }
+
+  if (do_seek_set (fp))
+    {
+      printf ("SEEK_SET test failed\n");
+      ret = 1;
+    }
+
+  /* Reopen the file.  */
+  fclose (fp);
+  fp = fopen (filename, "w+");
+  if (fp == NULL)
+    {
+      printf ("seek_end: fopen: %s\n", strerror (errno));
+      return 1;
+    }
+
+  if (do_seek_end (fp))
+    {
+      printf ("SEEK_END test failed\n");
+      ret = 1;
+    }
+
+  fclose (fp);
+
+  return ret;
+}
+
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
index b790029ffc7cb1e7879a51780abe2ad17ddc2819..d3f46b2ed1fa7af949a1163bd5c67295482b60d2 100644 (file)
@@ -545,6 +545,57 @@ _IO_wfile_sync (fp)
 }
 libc_hidden_def (_IO_wfile_sync)
 
+/* Adjust the internal buffer pointers to reflect the state in the external
+   buffer.  The content between fp->_IO_read_base and fp->_IO_read_ptr is
+   assumed to be converted and available in the range
+   fp->_wide_data->_IO_read_base and fp->_wide_data->_IO_read_end.
+
+   Returns 0 on success and -1 on error with the _IO_ERR_SEEN flag set.  */
+static inline int
+adjust_wide_data (_IO_FILE *fp, bool do_convert)
+{
+  struct _IO_codecvt *cv = fp->_codecvt;
+
+  int clen = (*cv->__codecvt_do_encoding) (cv);
+
+  /* Take the easy way out for constant length encodings if we don't need to
+     convert.  */
+  if (!do_convert && clen > 0)
+    {
+      fp->_wide_data->_IO_read_end += ((fp->_IO_read_ptr - fp->_IO_read_base)
+                                      / clen);
+      goto done;
+    }
+
+  enum __codecvt_result status;
+  const char *read_stop = (const char *) fp->_IO_read_base;
+  do
+    {
+
+      fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
+      status = (*cv->__codecvt_do_in) (cv, &fp->_wide_data->_IO_state,
+                                      fp->_IO_read_base, fp->_IO_read_ptr,
+                                      &read_stop,
+                                      fp->_wide_data->_IO_read_base,
+                                      fp->_wide_data->_IO_buf_end,
+                                      &fp->_wide_data->_IO_read_end);
+
+      /* Should we return EILSEQ?  */
+      if (__builtin_expect (status == __codecvt_error, 0))
+       {
+         fp->_flags |= _IO_ERR_SEEN;
+         return -1;
+       }
+    }
+  while (__builtin_expect (status == __codecvt_partial, 0));
+
+done:
+  /* Now seek to _IO_read_end to behave as if we have read it all in.  */
+  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
+
+  return 0;
+}
+
 _IO_off64_t
 _IO_wfile_seekoff (fp, offset, dir, mode)
      _IO_FILE *fp;
@@ -693,6 +744,10 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
                     fp->_wide_data->_IO_buf_base);
          _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
                     fp->_wide_data->_IO_buf_base);
+
+         if (adjust_wide_data (fp, false))
+           goto dumb;
+
          _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
          goto resync;
        }
@@ -733,6 +788,10 @@ _IO_wfile_seekoff (fp, offset, dir, mode)
   _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);
+
+  if (adjust_wide_data (fp, true))
+    goto dumb;
+
   fp->_offset = result + count;
   _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
   return offset;