summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2008-03-07 03:34:46 +0100
committerBruno Haible <bruno@clisp.org>2008-03-07 03:34:46 +0100
commit679b14d8851a364efdffbdf50d4aa43921762076 (patch)
tree5eaf0786820cc718844be79d3467669022f389d6
parentf9e70e3ef802527ebecd9179ca56cd19fe9946b6 (diff)
downloadgnulib-679b14d8851a364efdffbdf50d4aa43921762076.tar.gz
Make fflush after ungetc work on BSD platforms.
-rw-r--r--ChangeLog11
-rw-r--r--doc/posix-functions/fflush.texi9
-rw-r--r--lib/fflush.c42
-rw-r--r--modules/fflush-tests7
-rw-r--r--tests/test-fflush2.c78
-rwxr-xr-xtests/test-fflush2.sh8
6 files changed, 152 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index 61347900fb..78f3667256 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2008-03-06 Bruno Haible <bruno@clisp.org>
+
+ 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 <ebb9@byu.net>
Likewise for ftello.
diff --git a/doc/posix-functions/fflush.texi b/doc/posix-functions/fflush.texi
index 2cc9d88113..b1a461bf3b 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 be97769a4a..dfc1a74abd 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():
+ <http://www.opengroup.org/susv3/functions/fgetc.html>
+ "The file-position indicator is decremented by each successful
+ call to ungetc()..."
+ <http://www.opengroup.org/susv3/functions/ungetc.html>
+ "... the fgetc() function shall ... advance the associated file
+ position indicator for the stream ..."
+ 2) <http://www.opengroup.org/susv3/functions/ungetc.html> 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 d9ab967e44..d7ed9b1b66 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 0000000000..d5d8619c3c
--- /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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <stdlib.h>
+
+/* 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
+ <http://www.opengroup.org/susv3/functions/ungetc.html>:
+ "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 0000000000..5c3fc979a6
--- /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