summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2020-05-24 20:38:53 +0200
committerBruno Haible <bruno@clisp.org>2020-05-24 21:20:45 +0200
commit10cb4be2a2114dd6fff347acc9841d7904636adf (patch)
tree27cd0e13406412448ba2e4097d89780f66bf889a
parented8d8e847ab8c26165eaf8b446aee78c493dfce3 (diff)
downloadgnulib-10cb4be2a2114dd6fff347acc9841d7904636adf.tar.gz
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.
-rw-r--r--ChangeLog11
-rw-r--r--doc/posix-functions/fopen.texi19
-rw-r--r--lib/fopen.c92
-rw-r--r--m4/fopen.m487
-rw-r--r--modules/fopen-gnu27
5 files changed, 229 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index ae9fae472f..fab0d875a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
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.
* 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 308d67649b..6c562a6e58 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 ad6511d2c7..20065e4c6f 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 2a4b00d5d9..8eab4a699e 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 <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], [:])
diff --git a/modules/fopen-gnu b/modules/fopen-gnu
new file mode 100644
index 0000000000..f0f20548ee
--- /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:
+<stdio.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all