diff options
author | Siddhesh Poyarekar <siddhesh@redhat.com> | 2012-09-28 18:20:40 +0530 |
---|---|---|
committer | Siddhesh Poyarekar <siddhesh@redhat.com> | 2012-09-28 18:21:39 +0530 |
commit | 4573c6b09884a93fffa3a754678ef881cadebfb3 (patch) | |
tree | 89734fdb5480204f4bf4ad5c0977c39a5265ec2f | |
parent | 784421e72b926d027398ee93c6c2f1ddad549c6a (diff) | |
download | glibc-4573c6b09884a93fffa3a754678ef881cadebfb3.tar.gz glibc-4573c6b09884a93fffa3a754678ef881cadebfb3.tar.xz glibc-4573c6b09884a93fffa3a754678ef881cadebfb3.zip |
Adjust wide data buffer pointers during fseek and ftell
[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.
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | libio/Makefile | 3 | ||||
-rw-r--r-- | libio/tst-fseek.c | 173 | ||||
-rw-r--r-- | libio/wfileops.c | 59 |
5 files changed, 244 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog index 3004989b65..255e0751cf 100644 --- 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 c0a671de21..70ddcbe93c 100644 --- 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 diff --git a/libio/Makefile b/libio/Makefile index e2a7efdc1c..0d28cea59c 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -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 index 0000000000..457a087785 --- /dev/null +++ b/libio/tst-fseek.c @@ -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" diff --git a/libio/wfileops.c b/libio/wfileops.c index b790029ffc..d3f46b2ed1 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -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; |