]> Savannah Git Hosting - gnulib.git/commitdiff
fopen-gnu: New module.
authorBruno Haible <bruno@clisp.org>
Sun, 24 May 2020 18:38:53 +0000 (20:38 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 24 May 2020 19:20:45 +0000 (21:20 +0200)
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.

ChangeLog
doc/posix-functions/fopen.texi
lib/fopen.c
m4/fopen.m4
modules/fopen-gnu [new file with mode: 0644]

index ae9fae472f93a5f534e5c0612fd85962c85167ea..fab0d875a50ddf0e8eef5f826cad25a50617425e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+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.
index 308d67649bf3472c3209a54ec44cf72e81e58f0a..6c562a6e5850ba7c7c66fcc8f212c0f07009a320 100644 (file)
@@ -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
index ad6511d2c7d4eeb780e978b76d0db562b523b159..20065e4c6f76a766f41f5866e854f0e54b806131 100644 (file)
@@ -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);
 }
index 2a4b00d5d96c7561df6839ff33934ce58acea78b..8eab4a699e297a4de944652f648ab57b695a41b5 100644 (file)
@@ -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 (file)
index 0000000..f0f2054
--- /dev/null
@@ -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