@@ -64,7 +64,8 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \
bug-memstream1 bug-wmemstream1 \
tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
- tst-ftell-append tst-fputws tst-bz22415
+ tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \
+
ifeq (yes,$(build-shared))
# Add test-fopenloc only if shared library is enabled since it depends on
# shared localedata objects.
@@ -469,11 +469,10 @@ int
_IO_new_file_underflow (_IO_FILE *fp)
{
_IO_ssize_t count;
-#if 0
- /* SysV does not make this test; take it out for compatibility */
+
+ /* C99 requires EOF to be "sticky". */
if (fp->_flags & _IO_EOF_SEEN)
return (EOF);
-#endif
if (fp->_flags & _IO_NO_READS)
{
@@ -294,11 +294,10 @@ attribute_compat_text_section
_IO_old_file_underflow (_IO_FILE *fp)
{
_IO_ssize_t count;
-#if 0
- /* SysV does not make this test; take it out for compatibility */
+
+ /* C99 requires EOF to be "sticky". */
if (fp->_flags & _IO_EOF_SEEN)
return (EOF);
-#endif
if (fp->_flags & _IO_NO_READS)
{
new file mode 100644
@@ -0,0 +1,135 @@
+/* Bug 1190: EOF conditions are supposed to be sticky.
+ Copyright (C) 2018 Free Software Foundation.
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. This file is offered as-is,
+ without any warranty. */
+
+/* ISO C1999 specification of fgetc:
+
+ #include <stdio.h>
+ int fgetc (FILE *stream);
+
+ Description
+
+ If the end-of-file indicator for the input stream pointed to by
+ stream is not set and a next character is present, the fgetc
+ function obtains that character as an unsigned char converted to
+ an int and advances the associated file position indicator for
+ the stream (if defined).
+
+ Returns
+
+ If the end-of-file indicator for the stream is set, or if the
+ stream is at end-of-file, the end-of-file indicator for the
+ stream is set and the fgetc function returns EOF. Otherwise, the
+ fgetc function returns the next character from the input stream
+ pointed to by stream. If a read error occurs, the error indicator
+ for the stream is set and the fgetc function returns EOF.
+
+ The requirement to return EOF "if the end-of-file indicator for the
+ stream is set" was new in C99; the language in the 1989 edition of
+ the standard was ambiguous. Historically, BSD-derived Unix always
+ had the C99 behavior, whereas in System V fgetc would attempt to
+ call read() again before returning EOF again. Prior to version 2.28,
+ glibc followed the System V behavior even though this does not
+ comply with C99.
+
+ See
+ <https://sourceware.org/bugzilla/show_bug.cgi?id=1190>,
+ <https://sourceware.org/bugzilla/show_bug.cgi?id=19476>,
+ and the thread at
+ <https://sourceware.org/ml/libc-alpha/2012-09/msg00343.html>
+ for more detail. */
+
+#include <stdio.h>
+
+#include <support/temp_file.h>
+
+int
+do_test (void)
+{
+ /* Create a scratch file, then open it by name a second time,
+ so that at the operating system level we have two independent
+ seek pointers. */
+ char *scratch_fname;
+ int wfd = create_temp_file ("tst-fgetc-after-eof.", &scratch_fname);
+ if (wfd == -1)
+ {
+ perror ("create_temp_file");
+ return 1;
+ }
+
+ FILE *fout = fdopen (wfd, "w+");
+ if (!fout)
+ {
+ perror ("fdopen");
+ return 1;
+ }
+
+ if (fputs ("abc\n", fout) == EOF
+ || fflush (fout) || ferror (fout))
+ {
+ perror ("write error (initial)");
+ return 1;
+ }
+
+ FILE *fin = fopen (scratch_fname, "r+");
+ if (!fin)
+ {
+ perror (scratch_fname);
+ return 1;
+ }
+
+ /* Reach EOF. */
+ while (fgetc (fin) != EOF)
+ ;
+ if (ferror (fin))
+ {
+ perror ("read error");
+ return 1;
+ }
+
+ /* Enlarge the file using the write stream. */
+ if (fputs ("d\n", fout) == EOF
+ || fflush (fout) || ferror (fout))
+ {
+ perror ("write error (enlarging)");
+ return 1;
+ }
+
+ /* feof (fin) should still be true at this point. */
+ if (!feof (fin))
+ {
+ fputs ("FAIL: input stream not at EOF after enlarging", stderr);
+ return 1;
+ }
+
+ /* And consistent with that, another fgetc should return EOF. */
+ int c = fgetc (fin);
+ if (c != EOF)
+ {
+ fprintf (stderr, "FAIL: fgetc returned '%c', not EOF\n", c);
+ return 1;
+ }
+
+ /* However, after calling clearerr(), we should be able to read the
+ next characters written to the file. */
+ clearerr (fin);
+ c = fgetc (fin);
+ if (c != 'd')
+ {
+ if (c == EOF)
+ fprintf (stderr, "FAIL: fgetc after clearerr returned EOF, not 'd'\n");
+ else
+ fprintf (stderr,
+ "FAIL: fgetc after clearerr returned '%c', not 'd'\n", c);
+ return 1;
+ }
+
+ fclose (fin);
+ fclose (fout);
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -116,6 +116,10 @@ _IO_wfile_underflow (_IO_FILE *fp)
enum __codecvt_result status;
_IO_ssize_t count;
+ /* C99 requires EOF to be "sticky". */
+ if (fp->_flags & _IO_EOF_SEEN)
+ return (EOF);
+
if (__glibc_unlikely (fp->_flags & _IO_NO_READS))
{
fp->_flags |= _IO_ERR_SEEN;
@@ -50,7 +50,7 @@ strop-tests := wcscmp wcsncmp wmemcmp wcslen wcschr wcsrchr wcscpy wcsnlen \
tests := tst-wcstof wcsmbs-tst1 tst-wcsnlen tst-btowc tst-mbrtowc \
tst-wcrtomb tst-wcpncpy tst-mbsrtowcs tst-wchar-h tst-mbrtowc2 \
tst-c16c32-1 wcsatcliff tst-wcstol-locale tst-wcstod-nan-locale \
- tst-wcstod-round test-char-types \
+ tst-wcstod-round test-char-types tst-fgetwc-after-eof \
$(addprefix test-,$(strop-tests))
include ../Rules
new file mode 100644
@@ -0,0 +1,141 @@
+/* Bug 1190: EOF conditions are supposed to be sticky.
+ Copyright (C) 2018 Free Software Foundation.
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. This file is offered as-is,
+ without any warranty. */
+
+/* ISO C1999 specification of fgetwc:
+
+ #include <stdio.h>
+ #include <wchar.h>
+ wint_t fgetwc (FILE *stream);
+
+ Description
+
+ If the end-of-file indicator for the input stream pointed to by
+ stream is not set and a next wide character is present, the
+ fgetwc function obtains that wide character as a wchar_t
+ converted to a wint_t and advances the associated file position
+ indicator for the stream (if defined).
+
+ Returns
+
+ If the end-of-file indicator for the stream is set, or if the
+ stream is at end-of-file, the end- of-file indicator for the
+ stream is set and the fgetwc function returns WEOF. Otherwise,
+ the fgetwc function returns the next wide character from the
+ input stream pointed to by stream. If a read error occurs, the
+ error indicator for the stream is set and the fgetwc function
+ returns WEOF. If an encoding error occurs (including too few
+ bytes), the value of the macro EILSEQ is stored in errno and the
+ fgetwc function returns WEOF.
+
+ The requirement to return WEOF "if the end-of-file indicator for the
+ stream is set" was new in C99; the language in the 1995 edition of
+ the standard was ambiguous. Historically, BSD-derived Unix always
+ had the C99 behavior, whereas in System V fgetc would attempt to
+ call read() again before returning EOF again. Prior to version 2.28,
+ glibc followed the System V behavior even though this does not
+ comply with C99.
+
+ See
+ <https://sourceware.org/bugzilla/show_bug.cgi?id=1190>,
+ <https://sourceware.org/bugzilla/show_bug.cgi?id=19476>,
+ and the thread at
+ <https://sourceware.org/ml/libc-alpha/2012-09/msg00343.html>
+ for more detail. */
+
+#include <stdio.h>
+#include <wchar.h>
+
+#include <support/temp_file.h>
+
+int
+do_test (void)
+{
+ /* Create a scratch file, then open it by name a second time,
+ so that at the operating system level we have two independent
+ seek pointers. */
+ char *scratch_fname;
+ int wfd = create_temp_file ("tst-fgetc-after-eof.", &scratch_fname);
+ if (wfd == -1)
+ {
+ perror ("create_temp_file");
+ return 1;
+ }
+
+ FILE *fout = fdopen (wfd, "w+");
+ if (!fout)
+ {
+ perror ("fdopen");
+ return 1;
+ }
+
+ if (fputws (L"abc\n", fout) == WEOF
+ || fflush (fout) || ferror (fout))
+ {
+ perror ("write error (initial)");
+ return 1;
+ }
+
+ FILE *fin = fopen (scratch_fname, "r+");
+ if (!fin)
+ {
+ perror (scratch_fname);
+ return 1;
+ }
+
+ /* Reach EOF. */
+ while (fgetwc (fin) != WEOF)
+ ;
+ if (ferror (fin))
+ {
+ perror ("read error");
+ return 1;
+ }
+
+ /* Enlarge the file using the write stream. */
+ if (fputws (L"d\n", fout) == WEOF
+ || fflush (fout) || ferror (fout))
+ {
+ perror ("write error (enlarging)");
+ return 1;
+ }
+
+ /* feof (fin) should still be true at this point. */
+ if (!feof (fin))
+ {
+ fputs ("FAIL: input stream not at EOF after enlarging", stderr);
+ return 1;
+ }
+
+ /* And consistent with that, another fgetwc should return WEOF. */
+ wint_t c = fgetwc (fin);
+ if (c != WEOF)
+ {
+ fprintf (stderr, "FAIL: fgetc returned L'%lc', not WEOF\n", c);
+ return 1;
+ }
+
+ /* However, after calling clearerr(), we should be able to read the
+ next characters written to the file. */
+ clearerr (fin);
+ c = fgetwc (fin);
+ if (c != L'd')
+ {
+ if (c == EOF)
+ fprintf (stderr,
+ "FAIL: fgetwc after clearerr returned EOF, not L'd'\n");
+ else
+ fprintf (stderr,
+ "FAIL: fgetc after clearerr returned L'%lc', not L'd'\n", c);
+ return 1;
+ }
+
+ fclose (fin);
+ fclose (fout);
+ return 0;
+}
+
+#include <support/test-driver.c>