glibc/wcsmbs/tst-fgetwc-after-eof.c
Zack Weinberg 2cc7bad0ae [BZ 1190] Make EOF sticky in stdio.
C99 specifies that the EOF condition on a file is "sticky": once EOF
has been encountered, all subsequent reads should continue to return
EOF until the file is closed or something clears the "end-of-file
indicator" (e.g. fseek, clearerr).  This is arguably a change from
C89, where the wording was ambiguous; the BSDs always had sticky EOF,
but the System V lineage would attempt to read from the underlying fd
again.  GNU libc has followed System V for as long as we've been
using libio, but nowadays C99 conformance and BSD compatibility are
more important than System V compatibility.

You might wonder if changing the _underflow impls is sufficient to
apply the C99 semantics to all of the many stdio functions that
perform input.  It should be enough to cover all paths to _IO_SYSREAD,
and the only other functions that call _IO_SYSREAD are the _seekoff
impls, which is OK because seeking clears EOF, and the _xsgetn impls,
which, as far as I can tell, are unused within glibc.

The test programs in this patch use a pseudoterminal to set up the
necessary conditions.  To facilitate this I added a new test-support
function that sets up a pair of pty file descriptors for you; it's
almost the same as BSD openpty, the only differences are that it
allocates the optionally-returned tty pathname with malloc, and that
it crashes if anything goes wrong.

	[BZ #1190]
        [BZ #19476]
	* libio/fileops.c (_IO_new_file_underflow): Return EOF immediately
	if the _IO_EOF_SEEN bit is already set; update commentary.
	* libio/oldfileops.c (_IO_old_file_underflow): Likewise.
	* libio/wfileops.c (_IO_wfile_underflow): Likewise.

	* support/support_openpty.c, support/tty.h: New files.
	* support/Makefile (libsupport-routines): Add support_openpty.

	* libio/tst-fgetc-after-eof.c, wcsmbs/test-fgetwc-after-eof.c:
	New test cases.
	* libio/Makefile (tests): Add tst-fgetc-after-eof.
	* wcsmbs/Makefile (tests): Add tst-fgetwc-after-eof.
2018-03-13 08:31:56 -04:00

115 lines
3.9 KiB
C

/* 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 fgetwc 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 <support/tty.h>
#include <support/check.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#define XWRITE(fd, s, msg) do { \
if (write (fd, s, sizeof s - 1) != sizeof s - 1) \
{ \
perror ("write " msg); \
return 1; \
} \
} while (0)
int
do_test (void)
{
/* The easiest way to set up the conditions under which you can
notice whether the end-of-file indicator is sticky, is with a
pseudo-tty. This is also the case which applications are most
likely to care about. And it avoids any question of whether and
how it is legitimate to access the same physical file with two
independent FILE objects. */
int outer_fd, inner_fd;
FILE *fp;
support_openpty (&outer_fd, &inner_fd, 0, 0, 0);
fp = fdopen (inner_fd, "r+");
if (!fp)
{
perror ("fdopen");
return 1;
}
XWRITE (outer_fd, "abc\n\004", "first line + EOF");
TEST_COMPARE (fgetwc (fp), L'a');
TEST_COMPARE (fgetwc (fp), L'b');
TEST_COMPARE (fgetwc (fp), L'c');
TEST_COMPARE (fgetwc (fp), L'\n');
TEST_COMPARE (fgetwc (fp), WEOF);
TEST_VERIFY_EXIT (feof (fp));
TEST_VERIFY_EXIT (!ferror (fp));
XWRITE (outer_fd, "d\n", "second line");
/* At this point, there is a new full line of input waiting in the
kernelside input buffer, but we should still observe EOF from
stdio, because the end-of-file indicator has not been cleared. */
TEST_COMPARE (fgetwc (fp), WEOF);
/* Clearing EOF should reveal the next line of input. */
clearerr (fp);
TEST_COMPARE (fgetwc (fp), L'd');
TEST_COMPARE (fgetwc (fp), L'\n');
fclose (fp);
close (outer_fd);
return 0;
}
#include <support/test-driver.c>