autoupdate
[gnulib.git] / lib / dup3.c
index b9fb341..c61b9ba 100644 (file)
@@ -29,6 +29,8 @@
 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
 /* Native Woe32 API.  */
 
+# include <string.h>
+
 /* Get declarations of the Win32 API functions.  */
 # define WIN32_LEAN_AND_MEAN
 # include <windows.h>
 int
 dup3 (int oldfd, int newfd, int flags)
 {
+#if HAVE_DUP3
+# undef dup3
+  /* Try the system call first, if it exists.  (We may be running with a glibc
+     that has the function but with an older kernel that lacks it.)  */
+  {
+    /* Cache the information whether the system call really exists.  */
+    static int have_dup3_really; /* 0 = unknown, 1 = yes, -1 = no */
+    if (have_dup3_really >= 0)
+      {
+        int result = dup3 (oldfd, newfd, flags);
+        if (!(result < 0 && errno == ENOSYS))
+          {
+            have_dup3_really = 1;
+#if REPLACE_FCHDIR
+            if (0 <= result)
+              result = _gl_register_dup (oldfd, newfd);
+#endif
+            return result;
+          }
+        have_dup3_really = -1;
+      }
+  }
+#endif
+
   if (oldfd < 0 || newfd < 0 || newfd >= getdtablesize ())
     {
       errno = EBADF;
@@ -75,9 +101,9 @@ dup3 (int oldfd, int newfd, int flags)
   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.  */
+         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];
@@ -85,79 +111,83 @@ dup3 (int oldfd, int newfd, int flags)
       int result;
 
       if (old_handle == INVALID_HANDLE_VALUE)
-       {
-         /* oldfd is not open, or is an unassigned standard file
-            descriptor.  */
-         errno = EBADF;
-         return -1;
-       }
+        {
+          /* 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);
-       }
+        {
+          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;
+        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;
       }
 
+#if REPLACE_FCHDIR
+      if (result == newfd)
+        result = _gl_register_dup (oldfd, newfd);
+#endif
       return result;
     }
 
@@ -178,13 +208,13 @@ dup3 (int oldfd, int newfd, int flags)
       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;
-       }
+          || fcntl (newfd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1)
+        {
+          int saved_errno = errno;
+          close (newfd);
+          errno = saved_errno;
+          return -1;
+        }
     }
 
 #endif
@@ -196,5 +226,8 @@ dup3 (int oldfd, int newfd, int flags)
     setmode (newfd, O_TEXT);
 #endif
 
+#if REPLACE_FCHDIR
+  newfd = _gl_register_dup (oldfd, newfd);
+#endif
   return newfd;
 }