changeset 11886:3ac76948db10

New module 'dup3'.
author Bruno Haible <bruno@clisp.org>
date Mon, 24 Aug 2009 00:17:47 +0200
parents e61a5067af2f
children ce99ebbf27a3
files ChangeLog doc/glibc-functions/dup3.texi lib/dup3.c lib/unistd.in.h m4/dup3.m4 m4/unistd_h.m4 modules/dup3 modules/unistd
diffstat 8 files changed, 289 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+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.
--- a/doc/glibc-functions/dup3.texi
+++ b/doc/glibc-functions/dup3.texi
@@ -2,15 +2,15 @@
 @subsection @code{dup3}
 @findex dup3
 
-Gnulib module: ---
+Gnulib module: dup3
 
 Portability problems fixed by Gnulib:
 @itemize
-@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
+
+Portability problems not fixed by Gnulib:
+@itemize
+@end itemize
new file mode 100644
--- /dev/null
+++ b/lib/dup3.c
@@ -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;
+}
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -178,6 +178,27 @@
 #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
new file mode 100644
--- /dev/null
+++ b/m4/dup3.m4
@@ -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
+])
--- a/m4/unistd_h.m4
+++ b/m4/unistd_h.m4
@@ -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 @@
   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 @@
   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])
new file mode 100644
--- /dev/null
+++ b/modules/dup3
@@ -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
--- a/modules/unistd
+++ b/modules/unistd
@@ -28,6 +28,7 @@
 	      -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 @@
 	      -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' \