From 679b14d8851a364efdffbdf50d4aa43921762076 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Fri, 7 Mar 2008 03:34:46 +0100 Subject: [PATCH] Make fflush after ungetc work on BSD platforms. --- ChangeLog | 11 ++++++ doc/posix-functions/fflush.texi | 9 +++++ lib/fflush.c | 42 +++++++++++++++++++++- modules/fflush-tests | 7 ++-- tests/test-fflush2.c | 78 +++++++++++++++++++++++++++++++++++++++++ tests/test-fflush2.sh | 8 +++++ 6 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 tests/test-fflush2.c create mode 100755 tests/test-fflush2.sh diff --git a/ChangeLog b/ChangeLog index 61347900f..78f366725 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2008-03-06 Bruno Haible + + Make fflush after ungetc work on BSD platforms. + * lib/fflush.c (rpl_fflush): Discard ungetc buffer if possible. + * tests/test-fflush2.c: New file. + * tests/test-fflush2.sh: New file. + * modules/fflush-tests (Files): Add tests/test-fflush2.sh, + tests/test-fflush2.c. + (Makefile.am): Build test-fflush2 and run test-fflush2.sh. + * doc/posix-functions/fflush.texi: Document fflush after ungetc bug. + 2008-03-06 Eric Blake Likewise for ftello. diff --git a/doc/posix-functions/fflush.texi b/doc/posix-functions/fflush.texi index 2cc9d8811..b1a461bf3 100644 --- a/doc/posix-functions/fflush.texi +++ b/doc/posix-functions/fflush.texi @@ -15,6 +15,11 @@ It doesn't do this on some platforms. @item @code{fflush} on an input stream changes the position of the stream to the end of the previous buffer, on some platforms: mingw. +@item +@code{fflush} on an input stream right after @code{ungetc} does not discard +the @code{ungetc} buffer, on some platforms: +MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, Cygwin. +Cygwin. @end itemize Portability problems not fixed by Gnulib: @@ -26,4 +31,8 @@ contains Unix line terminators (LF), on some platforms: mingw. @item On Windows platforms (excluding Cygwin), this function does not set @code{errno} upon failure. +@item +@code{fflush} on an input stream right after @code{ungetc} does not discard +the @code{ungetc} buffer, on some platforms: +AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw. @end itemize diff --git a/lib/fflush.c b/lib/fflush.c index be97769a4..dfc1a74ab 100644 --- a/lib/fflush.c +++ b/lib/fflush.c @@ -1,5 +1,5 @@ /* fflush.c -- allow flushing input streams - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007-2008 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -59,6 +59,46 @@ rpl_fflush (FILE *stream) if (stream == NULL || ! freading (stream)) return fflush (stream); + /* Clear the ungetc buffer. + + This is needed before fetching the file-position indicator, because + 1) The file position indicator is incremented by fgetc() and decremented + by ungetc(): + + "The file-position indicator is decremented by each successful + call to ungetc()..." + + "... the fgetc() function shall ... advance the associated file + position indicator for the stream ..." + 2) says: + "The value of the file-position indicator for the stream after + reading or discarding all pushed-back bytes shall be the same + as it was before the bytes were pushed back." + 3) Here we are discarding all pushed-back bytes. + + Unfortunately it is impossible to implement this on platforms with + _IOERR, because an ungetc() on this platform prepends the pushed-back + bytes to the buffer without an indication of the limit between the + pushed-back bytes and the read-ahead bytes. */ +#if defined __sferror /* FreeBSD, NetBSD, OpenBSD, MacOS X, Cygwin */ + { +# if defined __NetBSD__ || defined __OpenBSD__ + struct __sfileext + { + struct __sbuf _ub; /* ungetc buffer */ + /* More fields, not relevant here. */ + }; + if (((struct __sfileext *) stream->_ext._base)->_ub._base != NULL) +# else + if (stream->_ub._base != NULL) +# endif + { + stream->_p += stream->_r; + stream->_r = 0; + } + } +#endif + /* POSIX does not specify fflush behavior for non-seekable input streams. Some implementations purge unread data, some return EBADF, some do nothing. */ diff --git a/modules/fflush-tests b/modules/fflush-tests index d9ab967e4..d7ed9b1b6 100644 --- a/modules/fflush-tests +++ b/modules/fflush-tests @@ -1,5 +1,7 @@ Files: tests/test-fflush.c +tests/test-fflush2.sh +tests/test-fflush2.c Depends-on: fseeko @@ -7,6 +9,7 @@ fseeko configure.ac: Makefile.am: -TESTS += test-fflush -check_PROGRAMS += test-fflush +TESTS += test-fflush test-fflush2.sh +TESTS_ENVIRONMENT += EXEEXT='@EXEEXT@' srcdir='$(srcdir)' +check_PROGRAMS += test-fflush test-fflush2 MOSTLYCLEANFILES += test-fflush.txt diff --git a/tests/test-fflush2.c b/tests/test-fflush2.c new file mode 100644 index 000000000..d5d8619c3 --- /dev/null +++ b/tests/test-fflush2.c @@ -0,0 +1,78 @@ +/* Test of POSIX compatible fflush() function. + Copyright (C) 2008 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include + +#include + +/* This test can only be made to work on specific platforms. */ +#if defined _IO_ferror_unlocked || defined __sferror /* GNU libc, BeOS; FreeBSD, NetBSD, OpenBSD, MacOS X, Cygwin */ +# define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + abort (); \ + } \ + } \ + while (0) +#else +# define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + printf ("Skipping test: expected failure on this platform\n"); \ + exit (77); \ + } \ + } \ + while (0) +#endif + +int +main (int argc, char **argv) +{ + /* Check that fflush after a non-backup ungetc() call discards the ungetc + buffer. This is mandated by POSIX + : + "The value of the file-position indicator for the stream after + reading or discarding all pushed-back bytes shall be the same + as it was before the bytes were pushed back." */ + int c; + + c = fgetc (stdin); + ASSERT (c == '#'); + + c = fgetc (stdin); + ASSERT (c == '!'); + + /* Here the file-position indicator must be 2. */ + + c = ungetc ('@', stdin); + ASSERT (c == '@'); + + fflush (stdin); + + /* Here the file-position indicator must be 2 again. */ + + c = fgetc (stdin); + ASSERT (c == '/'); + + return 0; +} diff --git a/tests/test-fflush2.sh b/tests/test-fflush2.sh new file mode 100755 index 000000000..5c3fc979a --- /dev/null +++ b/tests/test-fflush2.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Execute the test only with seekable input stream. +# The behaviour of fflush() on a non-seekable input stream is undefined. +./test-fflush2${EXEEXT} < "$srcdir/test-fflush2.sh" || exit $? +#cat "$srcdir/test-fflush2.sh" | ./test-fflush2${EXEEXT} || exit $? + +exit 0 -- 2.11.0