utimens: cache whether utimensat syscall works
[pspp] / lib / dup2.c
index e5d3de47b513bb3cbd5e6176d34fa53ea4d381c7..140af1b9fb4d8e7d19404c74b827dd2a5acedd22 100644 (file)
 #include <errno.h>
 #include <fcntl.h>
 
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Get declarations of the Win32 API functions.  */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
 #if REPLACE_DUP2
-/* On mingw, dup2 exists, but always returns 0 for success.  */
+
+# undef dup2
+
 int
-dup2 (int fd, int desired_fd)
-#undef dup2
+rpl_dup2 (int fd, int desired_fd)
 {
-  int result = dup2 (fd, desired_fd);
+  int result;
+# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  /* If fd is closed, mingw hangs on dup2 (fd, fd).  If fd is open,
+     dup2 (fd, fd) returns 0, but all further attempts to use fd in
+     future dup2 calls will hang.  */
+  if (fd == desired_fd)
+    {
+      if ((HANDLE) _get_osfhandle (fd) == INVALID_HANDLE_VALUE)
+        {
+          errno = EBADF;
+          return -1;
+        }
+      return fd;
+    }
+# endif
+  result = dup2 (fd, desired_fd);
+# ifdef __linux__
+  /* Correct a Linux return value.
+     <http://git.kernel.org/?p=linux/kernel/git/stable/linux-2.6.30.y.git;a=commitdiff;h=2b79bc4f7ebbd5af3c8b867968f9f15602d5f802>
+   */
+  if (fd == desired_fd && result == (unsigned int) -EBADF)
+    {
+      errno = EBADF;
+      result = -1;
+    }
+# endif
   if (result == 0)
     result = desired_fd;
+  /* Correct a cygwin 1.5.x errno value.  */
+  else if (result == -1 && errno == EMFILE)
+    errno = EBADF;
+#if REPLACE_FCHDIR
+  if (fd != desired_fd && result == desired_fd)
+    result = _gl_register_dup (fd, desired_fd);
+#endif
   return result;
 }
 
 #else /* !REPLACE_DUP2 */
+
 /* On older platforms, dup2 did not exist.  */
 
 # ifndef F_DUPFD
@@ -62,13 +102,19 @@ dupfd (int fd, int desired_fd)
 int
 dup2 (int fd, int desired_fd)
 {
+  int result;
   if (fd == desired_fd)
-    return fd;
+    return fcntl (fd, F_GETFL) < 0 ? -1 : fd;
   close (desired_fd);
 # ifdef F_DUPFD
-  return fcntl (fd, F_DUPFD, desired_fd);
+  result = fcntl (fd, F_DUPFD, desired_fd);
 # else
-  return dupfd (fd, desired_fd);
+  result = dupfd (fd, desired_fd);
 # endif
+#if REPLACE_FCHDIR
+  if (0 <= result)
+    result = _gl_register_dup (fd, desired_fd);
+#endif
+  return result;
 }
 #endif /* !REPLACE_DUP2 */