+2020-05-24 Bruno Haible <bruno@clisp.org>
+
+ fopen-gnu: New module.
+ Suggested by Tim Rühsen <tim.ruehsen@gmx.de> in
+ <https://lists.gnu.org/archive/html/bug-gnulib/2020-05/msg00119.html>.
+ * lib/fopen.c (rpl_fopen): When the fopen-gnu module is enabled and the
+ mode contains an 'x' or 'e' flag, use open() followed by fdopen().
+ * m4/fopen.m4 (gl_FUNC_FOPEN_GNU): New macro.
+ * modules/fopen-gnu: New file.
+ * doc/posix-functions/fopen.texi: Document the 'fopen-gnu' module.
+
2020-05-24 Bruno Haible <bruno@clisp.org>
open, openat: Really support O_CLOEXEC.
POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html}
-Gnulib module: fopen
+Gnulib module: fopen or fopen-gnu
-Portability problems fixed by Gnulib:
+Portability problems fixed by either Gnulib module @code{fopen} or @code{fopen-gnu}:
@itemize
@item
This function does not fail when the file name argument ends in a slash
recognize the @file{/dev/null} filename.
@end itemize
+Portability problems fixed by Gnulib module @code{fopen-gnu}:
+@itemize
+@item
+This function does not support the mode character
+@samp{x} (corresponding to @code{O_EXCL}), introduced in ISO C11,
+on some platforms:
+FreeBSD 8.2, NetBSD 6.1, OpenBSD 5.6, Minix 3.2, AIX 6.1, HP-UX 11.31, IRIX 6.5, Solaris 11.3, Cygwin 1.7.16 (2012), mingw, MSVC 14.
+@item
+This function does not support the mode character
+@samp{e} (corresponding to @code{O_CLOEXEC}),
+introduced into a future POSIX revision through
+@url{https://www.austingroupbugs.net/view.php?id=411}, on some platforms:
+glibc 2.6, Mac OS X 10.13, FreeBSD 9.0, NetBSD 5.1, OpenBSD 5.6, Minix 3.2, AIX 7.2, HP-UX 11.31, IRIX 6.5, Solaris 11.3, Cygwin 1.7.16 (2012), mingw, MSVC 14.
+@end itemize
+
Portability problems not fixed by Gnulib:
@itemize
@item
{
int open_direction;
int open_flags_standard;
+#if GNULIB_FOPEN_GNU
+ int open_flags_gnu;
+# define BUF_SIZE 80
+ char fdopen_mode_buf[BUF_SIZE + 1];
+#endif
+ int open_flags;
#if defined _WIN32 && ! defined __CYGWIN__
if (strcmp (filename, "/dev/null") == 0)
/* Parse the mode. */
open_direction = 0;
open_flags_standard = 0;
+#if GNULIB_FOPEN_GNU
+ open_flags_gnu = 0;
+#endif
{
- const char *m;
+ const char *p = mode;
+#if GNULIB_FOPEN_GNU
+ char *q = fdopen_mode_buf;
+#endif
- for (m = mode; *m != '\0'; m++)
+ for (; *p != '\0'; p++)
{
- switch (*m)
+ switch (*p)
{
case 'r':
open_direction = O_RDONLY;
+#if GNULIB_FOPEN_GNU
+ if (q < fdopen_mode_buf + BUF_SIZE)
+ *q++ = *p;
+#endif
continue;
case 'w':
open_direction = O_WRONLY;
open_flags_standard |= O_CREAT | O_TRUNC;
+#if GNULIB_FOPEN_GNU
+ if (q < fdopen_mode_buf + BUF_SIZE)
+ *q++ = *p;
+#endif
continue;
case 'a':
open_direction = O_WRONLY;
open_flags_standard |= O_CREAT | O_APPEND;
+#if GNULIB_FOPEN_GNU
+ if (q < fdopen_mode_buf + BUF_SIZE)
+ *q++ = *p;
+#endif
continue;
case 'b':
+#if GNULIB_FOPEN_GNU
+ if (q < fdopen_mode_buf + BUF_SIZE)
+ *q++ = *p;
+#endif
continue;
case '+':
open_direction = O_RDWR;
+#if GNULIB_FOPEN_GNU
+ if (q < fdopen_mode_buf + BUF_SIZE)
+ *q++ = *p;
+#endif
continue;
+#if GNULIB_FOPEN_GNU
+ case 'x':
+ open_flags_gnu |= O_EXCL;
+ continue;
+ case 'e':
+ open_flags_gnu |= O_CLOEXEC;
+ continue;
+#endif
default:
break;
}
+#if GNULIB_FOPEN_GNU
+ /* The rest of the mode string can be a platform-dependent extension.
+ Copy it unmodified. */
+ {
+ size_t len = strlen (p);
+ if (len > fdopen_mode_buf + BUF_SIZE - q)
+ len = fdopen_mode_buf + BUF_SIZE - q;
+ memcpy (q, p, len);
+ q += len;
+ }
+#endif
break;
}
+#if GNULIB_FOPEN_GNU
+ *q = '\0';
+#endif
}
+#if GNULIB_FOPEN_GNU
+ open_flags = open_flags_standard | open_flags_gnu;
+#else
+ open_flags = open_flags_standard;
+#endif
#if FOPEN_TRAILING_SLASH_BUG
/* Fail if the mode requires write access and the filename ends in a slash,
return NULL;
}
- fd = open (filename, open_direction | open_flags_standard);
+ fd = open (filename, open_direction | open_flags);
if (fd < 0)
return NULL;
return NULL;
}
+# if GNULIB_FOPEN_GNU
+ fp = fdopen (fd, fdopen_mode_buf);
+# else
fp = fdopen (fd, mode);
+# endif
if (fp == NULL)
{
int saved_errno = errno;
}
#endif
+#if GNULIB_FOPEN_GNU
+ if (open_flags_gnu != 0)
+ {
+ int fd;
+ FILE *fp;
+
+ fd = open (filename, open_direction | open_flags);
+ if (fd < 0)
+ return NULL;
+
+ fp = fdopen (fd, fdopen_mode_buf);
+ if (fp == NULL)
+ {
+ int saved_errno = errno;
+ close (fd);
+ errno = saved_errno;
+ }
+ return fp;
+ }
+#endif
+
return orig_fopen (filename, mode);
}
-# fopen.m4 serial 10
+# fopen.m4 serial 11
dnl Copyright (C) 2007-2020 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
esac
])
+AC_DEFUN([gl_FUNC_FOPEN_GNU],
+[
+ AC_REQUIRE([gl_FUNC_FOPEN])
+ AC_CACHE_CHECK([whether fopen supports the mode character 'x'],
+ [gl_cv_func_fopen_mode_x],
+ [rm -f conftest.x
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <errno.h>
+int main ()
+{
+ FILE *fp;
+ fp = fopen ("conftest.x", "w");
+ fclose (fp);
+ fp = fopen ("conftest.x", "wx");
+ if (fp != NULL)
+ /* 'x' ignored */
+ return 1;
+ else if (errno == EEXIST)
+ return 0;
+ else
+ /* 'x' rejected */
+ return 2;
+}]])],
+ [gl_cv_func_fopen_mode_x=yes],
+ [gl_cv_func_fopen_mode_x=no],
+ [case "$host_os" in
+ # Guess yes on glibc and musl systems.
+ linux*-gnu* | gnu* | kfreebsd*-gnu | *-musl*)
+ gl_cv_func_fopen_mode_x="guessing yes" ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *)
+ gl_cv_func_fopen_mode_x="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ rm -f conftest.x
+ ])
+ AC_CACHE_CHECK([whether fopen supports the mode character 'e'],
+ [gl_cv_func_fopen_mode_e],
+ [echo foo > conftest.x
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+int main ()
+{
+ FILE *fp = fopen ("conftest.x", "re");
+ if (fp != NULL)
+ {
+ if (fcntl (fileno (fp), F_GETFD) & FD_CLOEXEC)
+ return 0;
+ else
+ /* 'e' ignored */
+ return 1;
+ }
+ else
+ /* 'e' rejected */
+ return 2;
+}]])],
+ [gl_cv_func_fopen_mode_e=yes],
+ [gl_cv_func_fopen_mode_e=no],
+ [case "$host_os" in
+ # Guess yes on glibc and musl systems.
+ linux*-gnu* | gnu* | kfreebsd*-gnu | *-musl*)
+ gl_cv_func_fopen_mode_e="guessing yes" ;;
+ # Guess no on native Windows.
+ mingw*)
+ gl_cv_func_fopen_mode_e="guessing no" ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *)
+ gl_cv_func_fopen_mode_e="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ rm -f conftest.x
+ ])
+ case "$gl_cv_func_fopen_mode_x" in
+ *no) REPLACE_FOPEN=1 ;;
+ esac
+ case "$gl_cv_func_fopen_mode_e" in
+ *no) REPLACE_FOPEN=1 ;;
+ esac
+])
+
# Prerequisites of lib/fopen.c.
AC_DEFUN([gl_PREREQ_FOPEN], [:])
--- /dev/null
+Description:
+fopen() function: open a stream to a file, with GNU extensions.
+
+Files:
+
+Depends-on:
+fopen
+open [test $REPLACE_FOPEN = 1]
+
+configure.ac:
+gl_FUNC_FOPEN_GNU
+if test $REPLACE_FOPEN = 1; then
+ AC_LIBOBJ([fopen])
+ gl_PREREQ_FOPEN
+fi
+gl_MODULE_INDICATOR([fopen-gnu])
+
+Makefile.am:
+
+Include:
+<stdio.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all