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.
This commit is contained in:
Siddhesh Poyarekar 2012-09-28 18:20:40 +05:30
parent 784421e72b
commit 4573c6b098
5 changed files with 244 additions and 2 deletions

View File

@ -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]

2
NEWS
View File

@ -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

View 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

173
libio/tst-fseek.c Normal file
View File

@ -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"

View 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;