1/* Duplicate an open file descriptor to a specified file descriptor.
2
3   Copyright (C) 1999, 2004-2007, 2009-2014 Free Software Foundation, Inc.
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18/* written by Paul Eggert */
19
20#include <config.h>
21
22/* Specification.  */
23#include <unistd.h>
24
25#include <errno.h>
26#include <fcntl.h>
27
28#if HAVE_DUP2
29
30# undef dup2
31
32# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
33
34/* Get declarations of the native Windows API functions.  */
35#  define WIN32_LEAN_AND_MEAN
36#  include <windows.h>
37
38#  include "msvc-inval.h"
39
40/* Get _get_osfhandle.  */
41#  include "msvc-nothrow.h"
42
43static int
44ms_windows_dup2 (int fd, int desired_fd)
45{
46  int result;
47
48  /* If fd is closed, mingw hangs on dup2 (fd, fd).  If fd is open,
49     dup2 (fd, fd) returns 0, but all further attempts to use fd in
50     future dup2 calls will hang.  */
51  if (fd == desired_fd)
52    {
53      if ((HANDLE) _get_osfhandle (fd) == INVALID_HANDLE_VALUE)
54        {
55          errno = EBADF;
56          return -1;
57        }
58      return fd;
59    }
60
61  /* Wine 1.0.1 return 0 when desired_fd is negative but not -1:
62     http://bugs.winehq.org/show_bug.cgi?id=21289 */
63  if (desired_fd < 0)
64    {
65      errno = EBADF;
66      return -1;
67    }
68
69  TRY_MSVC_INVAL
70    {
71      result = dup2 (fd, desired_fd);
72    }
73  CATCH_MSVC_INVAL
74    {
75      errno = EBADF;
76      result = -1;
77    }
78  DONE_MSVC_INVAL;
79
80  if (result == 0)
81    result = desired_fd;
82
83  return result;
84}
85
86#  define dup2 ms_windows_dup2
87
88# endif
89
90int
91rpl_dup2 (int fd, int desired_fd)
92{
93  int result;
94
95# ifdef F_GETFL
96  /* On Linux kernels 2.6.26-2.6.29, dup2 (fd, fd) returns -EBADF.
97     On Cygwin 1.5.x, dup2 (1, 1) returns 0.
98     On Cygwin 1.7.17, dup2 (1, -1) dumps core.
99     On Cygwin 1.7.25, dup2 (1, 256) can dump core.
100     On Haiku, dup2 (fd, fd) mistakenly clears FD_CLOEXEC.  */
101#  if HAVE_SETDTABLESIZE
102  setdtablesize (desired_fd + 1);
103#  endif
104  if (desired_fd < 0)
105    fd = desired_fd;
106  if (fd == desired_fd)
107    return fcntl (fd, F_GETFL) == -1 ? -1 : fd;
108# endif
109
110  result = dup2 (fd, desired_fd);
111
112  /* Correct an errno value on FreeBSD 6.1 and Cygwin 1.5.x.  */
113  if (result == -1 && errno == EMFILE)
114    errno = EBADF;
115# if REPLACE_FCHDIR
116  if (fd != desired_fd && result != -1)
117    result = _gl_register_dup (fd, result);
118# endif
119  return result;
120}
121
122#else /* !HAVE_DUP2 */
123
124/* On older platforms, dup2 did not exist.  */
125
126# ifndef F_DUPFD
127static int
128dupfd (int fd, int desired_fd)
129{
130  int duplicated_fd = dup (fd);
131  if (duplicated_fd < 0 || duplicated_fd == desired_fd)
132    return duplicated_fd;
133  else
134    {
135      int r = dupfd (fd, desired_fd);
136      int e = errno;
137      close (duplicated_fd);
138      errno = e;
139      return r;
140    }
141}
142# endif
143
144int
145dup2 (int fd, int desired_fd)
146{
147  int result = fcntl (fd, F_GETFL) < 0 ? -1 : fd;
148  if (result == -1 || fd == desired_fd)
149    return result;
150  close (desired_fd);
151# ifdef F_DUPFD
152  result = fcntl (fd, F_DUPFD, desired_fd);
153#  if REPLACE_FCHDIR
154  if (0 <= result)
155    result = _gl_register_dup (fd, result);
156#  endif
157# else
158  result = dupfd (fd, desired_fd);
159# endif
160  if (result == -1 && (errno == EMFILE || errno == EINVAL))
161    errno = EBADF;
162  return result;
163}
164#endif /* !HAVE_DUP2 */
165