diff --git a/ChangeLog b/ChangeLog
index 26eb3d5..a7e03f2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2016-11-26 Paul Eggert
+
+ freopen: work around glibc bug with closed fd
+ Work around glibc bug#15589, where freopen mishandles the case
+ where stdin etc. are already closed.
+ * doc/posix-functions/freopen.texi (freopen): Document the bug.
+ * lib/freopen.c (_GL_ALREADY_INCLUDING_STDIO_H): Define this
+ instead of __need_FILE, as the latter does not work with glibc.
+ Include , for open flags.
+ (rpl_freopen): Work around glibc bug.
+ * m4/freopen.m4 (gl_FUNC_FREOPEN): Check for bug.
+ * modules/freopen (Depends-on): Add fcntl-h.
+ * tests/test-freopen.c (main): Test for bug.
+
2016-11-25 Paul Eggert
fnmatch: fix typo introduced on 2016-08-17
diff --git a/doc/posix-functions/freopen.texi b/doc/posix-functions/freopen.texi
index d14c3e1..f79a05a 100644
--- a/doc/posix-functions/freopen.texi
+++ b/doc/posix-functions/freopen.texi
@@ -9,6 +9,10 @@ Gnulib module: freopen
Portability problems fixed by Gnulib:
@itemize
@item
+On some platforms, if @code{stream} does not already have an open
+file descriptor, @code{freopen} returns the stream without opening
+the file: glibc 2.24.
address@hidden
On platforms where @code{off_t} is a 32-bit type, @code{freopen} may not work
correctly with files larger than 2 GB. (Cf. @code{AC_SYS_LARGEFILE}.)
@item
diff --git a/lib/freopen.c b/lib/freopen.c
index 4cf7528..229c1d9 100644
--- a/lib/freopen.c
+++ b/lib/freopen.c
@@ -19,12 +19,12 @@
/* If the user's config.h happens to include , let it include only
the system's here, so that orig_freopen doesn't recurse to
rpl_freopen. */
-#define __need_FILE
+#define _GL_ALREADY_INCLUDING_STDIO_H
#include
/* Get the original definition of freopen. It might be defined as a macro. */
#include
-#undef __need_FILE
+#undef _GL_ALREADY_INCLUDING_STDIO_H
#include
@@ -39,29 +39,54 @@ orig_freopen (const char *filename, const char *mode, FILE *stream)
this include because of the preliminary #include above. */
#include "stdio.h"
+#include
#include
+#include
FILE *
rpl_freopen (const char *filename, const char *mode, FILE *stream)
{
FILE *result;
-
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
- if (filename != NULL && strcmp (filename, "/dev/null") == 0)
- filename = "NUL";
+ char const *null_device = "NUL";
+ if (filename && strcmp (filename, "/dev/null") == 0)
+ filename = null_device;
+#else
+ char const *null_device = "/dev/null";
#endif
- /* Clear errno to check the success of freopen() with it */
+#ifdef __KLIBC__
errno = 0;
+#endif
result = orig_freopen (filename, mode, stream);
+ if (!result)
+ {
#ifdef __KLIBC__
- /* On OS/2 kLIBC, freopen() returns NULL even if it is successful
- if filename is NULL. */
- if (!filename && !result && !errno)
- result = stream;
+ /* On OS/2 kLIBC, freopen returns NULL even if it is successful
+ if filename is NULL. */
+ if (!filename && !errno)
+ result = stream;
#endif
+ }
+ else if (filename)
+ {
+ int fd = fileno (result);
+ if (dup2 (fd, fd) < 0 && errno == EBADF)
+ {
+ int nullfd = open (null_device, O_RDONLY | O_CLOEXEC);
+ int err = 0;
+ if (nullfd != fd)
+ {
+ if (dup2 (nullfd, fd) < 0)
+ err = 1;
+ close (nullfd);
+ }
+ if (!err)
+ result = orig_freopen (filename, mode, result);
+ }
+ }
return result;
}
diff --git a/m4/freopen.m4 b/m4/freopen.m4
index 8d8e124..727eb08 100644
--- a/m4/freopen.m4
+++ b/m4/freopen.m4
@@ -1,4 +1,4 @@
-# freopen.m4 serial 5
+# freopen.m4 serial 6
dnl Copyright (C) 2007-2016 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -12,6 +12,27 @@ AC_DEFUN([gl_FUNC_FREOPEN],
mingw* | pw* | os2*)
REPLACE_FREOPEN=1
;;
+ *)
+ AC_CACHE_CHECK([whether freopen works on closed fds],
+ [gl_cv_func_freopen_works_on_closed],
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include
+ #include
+ ]],
+ [[close (0);
+ return !(freopen ("/dev/null", "r", stdin)
+ && getchar () == EOF
+ && !ferror (stdin) && feof (stdin));]])],
+ [gl_cv_func_freopen_works_on_closed=yes],
+ [gl_cv_func_freopen_works_on_closed=no],
+ [case $host_os in
+ *gnu*) gl_cv_func_freopen_works_on_closed="guessing no" ;;
+ *) gl_cv_func_freopen_works_on_closed="guessing yes";;
+ esac])])
+ case $gl_cv_func_freopen_works_on_closed in
+ *no) REPLACE_FREOPEN=1;;
+ esac
esac
])
diff --git a/modules/freopen b/modules/freopen
index 958cdbb..adb4bbc 100644
--- a/modules/freopen
+++ b/modules/freopen
@@ -6,6 +6,7 @@ lib/freopen.c
m4/freopen.m4
Depends-on:
+fcntl-h [test $REPLACE_FREOPEN = 1]
stdio
largefile
diff --git a/tests/test-freopen.c b/tests/test-freopen.c
index 80453bf..849d7d1 100644
--- a/tests/test-freopen.c
+++ b/tests/test-freopen.c
@@ -33,7 +33,11 @@ main ()
{
const char *filename = "test-freopen.txt";
+ close (STDIN_FILENO);
ASSERT (freopen ("/dev/null", "r", stdin) != NULL);
+ ASSERT (getchar () == EOF);
+ ASSERT (!ferror (stdin));
+ ASSERT (feof (stdin));
#if 0 /* freopen (NULL, ...) is unsupported on most platforms. */
/* Test that freopen() sets errno if someone else closes the stream