New module 'dup3'.
authorBruno Haible <bruno@clisp.org>
Sun, 23 Aug 2009 22:17:47 +0000 (00:17 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 23 Aug 2009 22:17:47 +0000 (00:17 +0200)
ChangeLog
doc/glibc-functions/dup3.texi
lib/dup3.c [new file with mode: 0644]
lib/unistd.in.h
m4/dup3.m4 [new file with mode: 0644]
m4/unistd_h.m4
modules/dup3 [new file with mode: 0644]
modules/unistd

index 748419f..80a2e96 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2009-08-23  Bruno Haible  <bruno@clisp.org>
 
+       New module 'dup3'.
+       * lib/unistd.in.h (dup3): New declaration.
+       * lib/dup3.c: New file.
+       * m4/dup3.m4: New file.
+       * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize GNULIB_DUP3 and
+       HAVE_DUP3.
+       * modules/unistd (Makefile.am): Substitute GNULIB_DUP3 and HAVE_DUP3.
+       * modules/dup3: New file.
+       * doc/glibc-functions/dup3.texi: Mention the new module.
+
+2009-08-23  Bruno Haible  <bruno@clisp.org>
+
        Tweak the dup2 test.
        * tests/test-dup2.c (main): Create the test file empty. Verify that an
        out-of-range fd yields EBADF. Verify that after writing to /dev/null,
index 01dee9b..c48d76f 100644 (file)
@@ -2,15 +2,15 @@
 @subsection @code{dup3}
 @findex dup3
 
-Gnulib module: ---
+Gnulib module: dup3
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on all non-glibc platforms:
+MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin, mingw, Interix 3.5, BeOS.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on all non-glibc platforms:
-MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin, mingw, Interix 3.5, BeOS.
 @end itemize
diff --git a/lib/dup3.c b/lib/dup3.c
new file mode 100644 (file)
index 0000000..b9fb341
--- /dev/null
@@ -0,0 +1,200 @@
+/* Copy a file descriptor, applying specific flags.
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "binary-io.h"
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Native Woe32 API.  */
+
+/* Get declarations of the Win32 API functions.  */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+/* Upper bound on getdtablesize().  See lib/getdtablesize.c.  */
+# define OPEN_MAX_MAX 0x10000
+
+#else
+/* Unix API.  */
+
+# ifndef O_CLOEXEC
+#  define O_CLOEXEC 0
+# endif
+
+#endif
+
+int
+dup3 (int oldfd, int newfd, int flags)
+{
+  if (oldfd < 0 || newfd < 0 || newfd >= getdtablesize ())
+    {
+      errno = EBADF;
+      return -1;
+    }
+
+  if (newfd == oldfd)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* Check the supported flags.
+     Note that O_NONBLOCK is not supported, because setting it on newfd
+     would implicitly also set it on oldfd.  */
+  if ((flags & ~(O_CLOEXEC | O_BINARY | O_TEXT)) != 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Native Woe32 API.  */
+
+  if (flags & O_CLOEXEC)
+    {
+      /* Neither dup() nor dup2() can create a file descriptor with
+        O_CLOEXEC = O_NOINHERIT set.  We need to use the low-level function
+        _open_osfhandle for this.  Iterate until all file descriptors less
+        than newfd are filled up.  */
+      HANDLE curr_process = GetCurrentProcess ();
+      HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
+      unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
+      unsigned int fds_to_close_bound = 0;
+      int result;
+
+      if (old_handle == INVALID_HANDLE_VALUE)
+       {
+         /* oldfd is not open, or is an unassigned standard file
+            descriptor.  */
+         errno = EBADF;
+         return -1;
+       }
+
+      close (newfd);
+
+      for (;;)
+       {
+         HANDLE new_handle;
+         int duplicated_fd;
+         unsigned int index;
+
+         if (!DuplicateHandle (curr_process,         /* SourceProcessHandle */
+                               old_handle,           /* SourceHandle */
+                               curr_process,         /* TargetProcessHandle */
+                               (PHANDLE) &new_handle, /* TargetHandle */
+                               (DWORD) 0,            /* DesiredAccess */
+                               FALSE,                /* InheritHandle */
+                               DUPLICATE_SAME_ACCESS)) /* Options */
+           {
+             errno = EBADF; /* arbitrary */
+             result = -1;
+             break;
+           }
+         duplicated_fd = _open_osfhandle ((long) new_handle, flags);
+         if (duplicated_fd < 0)
+           {
+             CloseHandle (new_handle);
+             result = -1;
+             break;
+           }
+         if (duplicated_fd > newfd)
+           /* Shouldn't happen, since newfd is still closed.  */
+           abort ();
+         if (duplicated_fd == newfd)
+           {
+             result = newfd;
+             break;
+           }
+
+         /* Set the bit duplicated_fd in fds_to_close[].  */
+         index = (unsigned int) duplicated_fd / CHAR_BIT;
+         if (index >= fds_to_close_bound)
+           {
+             if (index >= sizeof (fds_to_close))
+               /* Need to increase OPEN_MAX_MAX.  */
+               abort ();
+             memset (fds_to_close + fds_to_close_bound, '\0',
+                     index + 1 - fds_to_close_bound);
+             fds_to_close_bound = index + 1;
+           }
+         fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
+       }
+
+      /* Close the previous fds that turned out to be too small.  */
+      {
+       int saved_errno = errno;
+       unsigned int duplicated_fd;
+
+       for (duplicated_fd = 0;
+            duplicated_fd < fds_to_close_bound * CHAR_BIT;
+            duplicated_fd++)
+         if ((fds_to_close[duplicated_fd / CHAR_BIT]
+              >> (duplicated_fd % CHAR_BIT))
+             & 1)
+           close (duplicated_fd);
+
+       errno = saved_errno;
+      }
+
+      return result;
+    }
+
+  if (dup2 (oldfd, newfd) < 0)
+    return -1;
+
+#else
+/* Unix API.  */
+
+  if (dup2 (oldfd, newfd) < 0)
+    return -1;
+
+  /* POSIX <http://www.opengroup.org/onlinepubs/9699919799/functions/dup.html>
+     says that initially, the FD_CLOEXEC flag is cleared on newfd.  */
+
+  if (flags & O_CLOEXEC)
+    {
+      int fcntl_flags;
+
+      if ((fcntl_flags = fcntl (newfd, F_GETFD, 0)) < 0
+         || fcntl (newfd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1)
+       {
+         int saved_errno = errno;
+         close (newfd);
+         errno = saved_errno;
+         return -1;
+       }
+    }
+
+#endif
+
+#if O_BINARY
+  if (flags & O_BINARY)
+    setmode (newfd, O_BINARY);
+  else if (flags & O_TEXT)
+    setmode (newfd, O_TEXT);
+#endif
+
+  return newfd;
+}
index a86935b..7dd9834 100644 (file)
@@ -178,6 +178,27 @@ extern int dup2 (int oldfd, int newfd);
 #endif
 
 
+#if @GNULIB_DUP3@
+# if !@HAVE_DUP3@
+/* Copy the file descriptor OLDFD into file descriptor NEWFD, with the
+   specified flags.
+   The flags are a bitmask, possibly including O_CLOEXEC (defined in <fcntl.h>)
+   and O_TEXT, O_BINARY (defined in "binary-io.h").
+   Close NEWFD first if it is open.
+   Return newfd if successful, otherwise -1 and errno set.
+   See the Linux man page at
+   <http://www.kernel.org/doc/man-pages/online/pages/man2/dup3.2.html>.  */
+extern int dup3 (int oldfd, int newfd, int flags);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef dup3
+# define dup3(o,n,f) \
+    (GL_LINK_WARNING ("dup3 is unportable - " \
+                      "use gnulib module dup3 for portability"), \
+     dup3 (o, n, f))
+#endif
+
+
 #if @GNULIB_ENVIRON@
 # if !@HAVE_DECL_ENVIRON@
 /* Set of environment variables and values.  An array of strings of the form
diff --git a/m4/dup3.m4 b/m4/dup3.m4
new file mode 100644 (file)
index 0000000..e4fe7a6
--- /dev/null
@@ -0,0 +1,19 @@
+# dup3.m4 serial 1
+dnl Copyright (C) 2009 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_DUP3],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare dup3().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS_ONCE([dup3])
+  if test $ac_cv_func_dup3 != yes; then
+    HAVE_DUP3=0
+    AC_LIBOBJ([dup3])
+  fi
+])
index bff2f28..53fc330 100644 (file)
@@ -1,4 +1,4 @@
-# unistd_h.m4 serial 20
+# unistd_h.m4 serial 21
 dnl Copyright (C) 2006-2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -35,6 +35,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
   GNULIB_CHOWN=0;            AC_SUBST([GNULIB_CHOWN])
   GNULIB_CLOSE=0;            AC_SUBST([GNULIB_CLOSE])
   GNULIB_DUP2=0;             AC_SUBST([GNULIB_DUP2])
+  GNULIB_DUP3=0;             AC_SUBST([GNULIB_DUP3])
   GNULIB_ENVIRON=0;          AC_SUBST([GNULIB_ENVIRON])
   GNULIB_EUIDACCESS=0;       AC_SUBST([GNULIB_EUIDACCESS])
   GNULIB_FCHDIR=0;           AC_SUBST([GNULIB_FCHDIR])
@@ -58,6 +59,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
   GNULIB_WRITE=0;            AC_SUBST([GNULIB_WRITE])
   dnl Assume proper GNU behavior unless another module says otherwise.
   HAVE_DUP2=1;            AC_SUBST([HAVE_DUP2])
+  HAVE_DUP3=1;            AC_SUBST([HAVE_DUP3])
   HAVE_EUIDACCESS=1;      AC_SUBST([HAVE_EUIDACCESS])
   HAVE_FSYNC=1;           AC_SUBST([HAVE_FSYNC])
   HAVE_FTRUNCATE=1;       AC_SUBST([HAVE_FTRUNCATE])
diff --git a/modules/dup3 b/modules/dup3
new file mode 100644 (file)
index 0000000..38bdcf0
--- /dev/null
@@ -0,0 +1,27 @@
+Description:
+dup3() function: copy a file descriptor, applying specific flags.
+
+Files:
+lib/dup3.c
+m4/dup3.m4
+
+Depends-on:
+unistd
+fcntl
+binary-io
+getdtablesize
+
+configure.ac:
+gl_FUNC_DUP3
+gl_UNISTD_MODULE_INDICATOR([dup3])
+
+Makefile.am:
+
+Include:
+<unistd.h>
+
+License:
+LGPL
+
+Maintainer:
+Bruno Haible, Jim Meyering
index 7fed07d..66fc1a3 100644 (file)
@@ -28,6 +28,7 @@ unistd.h: unistd.in.h
              -e 's|@''GNULIB_CHOWN''@|$(GNULIB_CHOWN)|g' \
              -e 's|@''GNULIB_CLOSE''@|$(GNULIB_CLOSE)|g' \
              -e 's|@''GNULIB_DUP2''@|$(GNULIB_DUP2)|g' \
+             -e 's|@''GNULIB_DUP3''@|$(GNULIB_DUP3)|g' \
              -e 's|@''GNULIB_ENVIRON''@|$(GNULIB_ENVIRON)|g' \
              -e 's|@''GNULIB_EUIDACCESS''@|$(GNULIB_EUIDACCESS)|g' \
              -e 's|@''GNULIB_FCHDIR''@|$(GNULIB_FCHDIR)|g' \
@@ -50,6 +51,7 @@ unistd.h: unistd.in.h
              -e 's|@''GNULIB_UNISTD_H_SIGPIPE''@|$(GNULIB_UNISTD_H_SIGPIPE)|g' \
              -e 's|@''GNULIB_WRITE''@|$(GNULIB_WRITE)|g' \
              -e 's|@''HAVE_DUP2''@|$(HAVE_DUP2)|g' \
+             -e 's|@''HAVE_DUP3''@|$(HAVE_DUP3)|g' \
              -e 's|@''HAVE_EUIDACCESS''@|$(HAVE_EUIDACCESS)|g' \
              -e 's|@''HAVE_FSYNC''@|$(HAVE_FSYNC)|g' \
              -e 's|@''HAVE_FTRUNCATE''@|$(HAVE_FTRUNCATE)|g' \