# HG changeset patch # User Bruno Haible # Date 1251065867 -7200 # Node ID 3ac76948db10d15756a4a8f3f8de852664b545e3 # Parent e61a5067af2fa026d0ec8d7a3c04c21ed2e14700 New module 'dup3'. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2009-08-23 Bruno Haible + + 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 Tweak the dup2 test. diff --git a/doc/glibc-functions/dup3.texi b/doc/glibc-functions/dup3.texi --- 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 diff --git a/lib/dup3.c b/lib/dup3.c 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 + +/* Specification. */ +#include + +#include +#include +#include + +#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 + +/* 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 + 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; +} diff --git a/lib/unistd.in.h b/lib/unistd.in.h --- 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 ) + 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 + . */ +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 --- /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 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 +]) diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4 --- 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]) diff --git a/modules/dup3 b/modules/dup3 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: + + +License: +LGPL + +Maintainer: +Bruno Haible, Jim Meyering diff --git a/modules/unistd b/modules/unistd --- 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' \