diff options
Diffstat (limited to 'libc/libio')
-rw-r--r-- | libc/libio/Makefile | 4 | ||||
-rw-r--r-- | libc/libio/filedoalloc.c | 2 | ||||
-rw-r--r-- | libc/libio/fileops.c | 29 | ||||
-rw-r--r-- | libc/libio/libio.h | 2 | ||||
-rw-r--r-- | libc/libio/oldfileops.c | 2 | ||||
-rw-r--r-- | libc/libio/tst-fseek.c | 173 | ||||
-rw-r--r-- | libc/libio/wfileops.c | 177 |
7 files changed, 355 insertions, 34 deletions
diff --git a/libc/libio/Makefile b/libc/libio/Makefile index d66e090e6..ae1621fbd 100644 --- a/libc/libio/Makefile +++ b/libc/libio/Makefile @@ -77,7 +77,8 @@ tests-$(OPTION_POSIX_WIDE_CHAR_DEVICE_IO) \ += bug-rewind bug-rewind2 bug-ungetwc1 \ bug-wfflush bug-wmemstream1 tst-fopenloc2 \ tst_getwc \ - tst_putwc tst_wprintf tst_wprintf2 tst_wscanf tst-fgetwc bug-wsetpos + tst_putwc tst_wprintf tst_wprintf2 tst_wscanf \ + tst-fgetwc bug-wsetpos tst-fseek tests-$(OPTION_POSIX_C_LANG_WIDE_CHAR) \ += tst_swprintf tst_swscanf \ tst-sscanf \ @@ -180,6 +181,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/libc/libio/filedoalloc.c b/libc/libio/filedoalloc.c index b7733c7f8..c9b0f0172 100644 --- a/libc/libio/filedoalloc.c +++ b/libc/libio/filedoalloc.c @@ -96,7 +96,7 @@ _IO_file_doallocate (fp) { _IO_size_t size; char *p; - struct _G_stat64 st; + struct stat64 st; #ifndef _LIBC /* If _IO_cleanup_registration_needed is non-zero, we should call the diff --git a/libc/libio/fileops.c b/libc/libio/fileops.c index a65f0afbc..7033dd165 100644 --- a/libc/libio/fileops.c +++ b/libc/libio/fileops.c @@ -643,7 +643,7 @@ libc_hidden_ver (_IO_new_file_underflow, _IO_file_underflow) static int mmap_remap_check (_IO_FILE *fp) { - struct _G_stat64 st; + struct stat64 st; if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode) && st.st_size != 0 @@ -771,7 +771,7 @@ decide_maybe_mmap (_IO_FILE *fp) file descriptors are for mmap-able objects and on 32-bit machines we don't want to map files which are too large since this would require too much virtual memory. */ - struct _G_stat64 st; + struct stat64 st; if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode) && st.st_size != 0 @@ -986,6 +986,9 @@ _IO_new_file_seekoff (fp, offset, dir, mode) 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. */ @@ -996,10 +999,8 @@ _IO_new_file_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->_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) { @@ -1018,7 +1019,17 @@ _IO_new_file_seekoff (fp, offset, dir, mode) { 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) @@ -1046,7 +1057,7 @@ _IO_new_file_seekoff (fp, offset, dir, mode) break; case _IO_seek_end: { - struct _G_stat64 st; + struct stat64 st; if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode)) { offset += st.st_size; @@ -1247,7 +1258,7 @@ _IO_file_stat (fp, st) void *st; { #ifdef _G_FSTAT64 - return _G_FSTAT64 (fp->_fileno, (struct _G_stat64 *) st); + return _G_FSTAT64 (fp->_fileno, (struct stat64 *) st); #else return fstat (fp->_fileno, (struct stat *) st); #endif diff --git a/libc/libio/libio.h b/libc/libio/libio.h index f093a93e9..45f4707e6 100644 --- a/libc/libio/libio.h +++ b/libc/libio/libio.h @@ -37,7 +37,7 @@ #define _IO_size_t size_t #define _IO_ssize_t __ssize_t #define _IO_off_t __off_t -#define _IO_off64_t _G_off64_t +#define _IO_off64_t __off64_t #define _IO_pid_t __pid_t #define _IO_uid_t __uid_t #define _IO_iconv_t _G_iconv_t diff --git a/libc/libio/oldfileops.c b/libc/libio/oldfileops.c index 8fda0484a..aa4e3f5f9 100644 --- a/libc/libio/oldfileops.c +++ b/libc/libio/oldfileops.c @@ -530,7 +530,7 @@ _IO_old_file_seekoff (fp, offset, dir, mode) break; case _IO_seek_end: { - struct _G_stat64 st; + struct stat64 st; if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode)) { offset += st.st_size; diff --git a/libc/libio/tst-fseek.c b/libc/libio/tst-fseek.c new file mode 100644 index 000000000..457a08778 --- /dev/null +++ b/libc/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/libc/libio/wfileops.c b/libc/libio/wfileops.c index b790029ff..44b1236c0 100644 --- a/libc/libio/wfileops.c +++ b/libc/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; @@ -562,6 +613,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 @@ -593,11 +648,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) { @@ -628,29 +680,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; @@ -660,7 +787,7 @@ _IO_wfile_seekoff (fp, offset, dir, mode) break; case _IO_seek_end: { - struct _G_stat64 st; + struct stat64 st; if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode)) { offset += st.st_size; @@ -693,6 +820,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 +864,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; |