From 10cb4be2a2114dd6fff347acc9841d7904636adf Mon Sep 17 00:00:00 2001
From: Bruno Haible
Date: Sun, 24 May 2020 20:38:53 +0200
Subject: [PATCH 1/2] fopen-gnu: New module.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Suggested by Tim Rühsen in
.
* 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.
---
ChangeLog | 11 +++++
doc/posix-functions/fopen.texi | 19 ++++++++-
lib/fopen.c | 92 ++++++++++++++++++++++++++++++++++++++++--
m4/fopen.m4 | 87 ++++++++++++++++++++++++++++++++++++++-
modules/fopen-gnu | 27 +++++++++++++
5 files changed, 229 insertions(+), 7 deletions(-)
create mode 100644 modules/fopen-gnu
diff --git a/ChangeLog b/ChangeLog
index ae9fae4..fab0d87 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
2020-05-24 Bruno Haible
+ fopen-gnu: New module.
+ Suggested by Tim Rühsen in
+ .
+ * 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
+
open, openat: Really support O_CLOEXEC.
* lib/open.c (open): When have_cloexec is still undecided, do pass a
O_CLOEXEC flag to orig_open.
diff --git a/doc/posix-functions/fopen.texi b/doc/posix-functions/fopen.texi
index 308d676..6c562a6 100644
--- a/doc/posix-functions/fopen.texi
+++ b/doc/posix-functions/fopen.texi
@@ -4,9 +4,9 @@
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
@@ -21,6 +21,21 @@ On Windows platforms (excluding Cygwin), this function does usually not
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
diff --git a/lib/fopen.c b/lib/fopen.c
index ad6511d..20065e4 100644
--- a/lib/fopen.c
+++ b/lib/fopen.c
@@ -49,6 +49,12 @@ rpl_fopen (const char *filename, const char *mode)
{
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)
@@ -58,35 +64,88 @@ rpl_fopen (const char *filename, const char *mode)
/* 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,
@@ -116,7 +175,7 @@ rpl_fopen (const char *filename, const char *mode)
return NULL;
}
- fd = open (filename, open_direction | open_flags_standard);
+ fd = open (filename, open_direction | open_flags);
if (fd < 0)
return NULL;
@@ -127,7 +186,11 @@ rpl_fopen (const char *filename, const char *mode)
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;
@@ -139,5 +202,26 @@ rpl_fopen (const char *filename, const char *mode)
}
#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);
}
diff --git a/m4/fopen.m4 b/m4/fopen.m4
index 2a4b00d..8eab4a6 100644
--- a/m4/fopen.m4
+++ b/m4/fopen.m4
@@ -1,4 +1,4 @@
-# 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,
@@ -58,5 +58,90 @@ changequote([,])dnl
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
+#include
+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
+#include
+#include
+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], [:])
diff --git a/modules/fopen-gnu b/modules/fopen-gnu
new file mode 100644
index 0000000..f0f2054
--- /dev/null
+++ b/modules/fopen-gnu
@@ -0,0 +1,27 @@
+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:
+
+
+License:
+LGPLv2+
+
+Maintainer:
+all
--
2.7.4