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