1/* Copyright (C) 1993,1994,1997-1999,2000,2002 Free Software Foundation, Inc.
2   This file is part of the GNU C Library.
3
4   The GNU C Library is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Lesser General Public
6   License as published by the Free Software Foundation; either
7   version 2.1 of the License, or (at your option) any later version.
8
9   The GNU C Library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with the GNU C Library; if not, write to the Free
16   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17   02111-1307 USA.
18
19   As a special exception, if you link the code in this file with
20   files compiled with a GNU compiler to produce an executable,
21   that does not cause the resulting executable to be covered by
22   the GNU Lesser General Public License.  This exception does not
23   however invalidate any other reasons why the executable file
24   might be covered by the GNU Lesser General Public License.
25   This exception applies to code released by its copyright holders
26   in files containing the exception.
27*/
28
29
30#include "libioP.h"
31#include <stdlib.h>
32#include <fcntl.h>
33
34#ifdef _LIBC
35# include <shlib-compat.h>
36#endif
37
38
39_IO_FILE *
40_IO_new_fdopen (int fd, const char *mode)
41{
42  int read_write;
43  int posix_mode = 0;
44  struct locked_FILE
45  {
46    struct _IO_FILE_plus fp;
47#ifdef _IO_MTSAFE_IO
48    _IO_lock_t lock;
49#endif
50    struct _IO_wide_data wd;
51  } *new_f;
52  int fd_flags;
53  int i;
54  int use_mmap = 0;
55
56  switch (*mode)
57    {
58    case 'r':
59      read_write = _IO_NO_WRITES;
60      break;
61    case 'w':
62      read_write = _IO_NO_READS;
63      break;
64    case 'a':
65      posix_mode = O_APPEND;
66      read_write = _IO_NO_READS|_IO_IS_APPENDING;
67      break;
68    default:
69      MAYBE_SET_EINVAL;
70      return NULL;
71  }
72  for (i = 1; i < 5; ++i)
73    {
74      switch (*++mode)
75	{
76	case '\0':
77	  break;
78	case '+':
79	  read_write &= _IO_IS_APPENDING;
80	  break;
81	case 'm':
82	  use_mmap = 1;
83	  continue;
84	case 'x':
85	case 'b':
86	default:
87	  /* Ignore */
88	  continue;
89	}
90      break;
91    }
92#ifdef F_GETFL
93  fd_flags = fcntl(fd, F_GETFL);
94#ifndef O_ACCMODE
95#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
96#endif
97  if (fd_flags == -1)
98    return NULL;
99
100  if (((fd_flags & O_ACCMODE) == O_RDONLY && !(read_write & _IO_NO_WRITES))
101      || ((fd_flags & O_ACCMODE) == O_WRONLY && !(read_write & _IO_NO_READS)))
102    {
103      MAYBE_SET_EINVAL;
104      return NULL;
105    }
106
107  /* The May 93 draft of P1003.4/D14.1 (redesignated as 1003.1b)
108     [System Application Program Interface (API) Amendment 1:
109     Realtime Extensions], Rationale B.8.3.3
110     Open a Stream on a File Descriptor says:
111
112         Although not explicitly required by POSIX.1, a good
113         implementation of append ("a") mode would cause the
114         O_APPEND flag to be set.
115
116     (Historical implementations [such as Solaris2] do a one-time
117     seek in fdopen.)
118
119     However, we do not turn O_APPEND off if the mode is "w" (even
120     though that would seem consistent) because that would be more
121     likely to break historical programs.
122     */
123  if ((posix_mode & O_APPEND) && !(fd_flags & O_APPEND))
124    {
125#ifdef F_SETFL
126      if (fcntl(fd, F_SETFL, fd_flags | O_APPEND) == -1)
127#endif
128	return NULL;
129    }
130#endif
131
132  new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));
133  if (new_f == NULL)
134    return NULL;
135#ifdef _IO_MTSAFE_IO
136  new_f->fp.file._lock = &new_f->lock;
137#endif
138  /* Set up initially to use the `maybe_mmap' jump tables rather than using
139     __fopen_maybe_mmap to do it, because we need them in place before we
140     call _IO_file_attach or else it will allocate a buffer immediately.  */
141  _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd,
142#ifdef _G_HAVE_MMAP
143	       (use_mmap && (read_write & _IO_NO_WRITES))
144	       ? &_IO_wfile_jumps_maybe_mmap :
145#endif
146	       &INTUSE(_IO_wfile_jumps));
147  _IO_JUMPS (&new_f->fp) =
148#ifdef _G_HAVE_MMAP
149    (use_mmap && (read_write & _IO_NO_WRITES)) ? &_IO_file_jumps_maybe_mmap :
150#endif
151      &INTUSE(_IO_file_jumps);
152  INTUSE(_IO_file_init) (&new_f->fp);
153#if  !_IO_UNIFIED_JUMPTABLES
154  new_f->fp.vtable = NULL;
155#endif
156  if (INTUSE(_IO_file_attach) ((_IO_FILE *) &new_f->fp, fd) == NULL)
157    {
158      INTUSE(_IO_setb) (&new_f->fp.file, NULL, NULL, 0);
159      INTUSE(_IO_un_link) (&new_f->fp);
160      free (new_f);
161      return NULL;
162    }
163  new_f->fp.file._flags &= ~_IO_DELETE_DONT_CLOSE;
164
165  new_f->fp.file._IO_file_flags =
166    _IO_mask_flags (&new_f->fp.file, read_write,
167		    _IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
168
169  return &new_f->fp.file;
170}
171INTDEF2(_IO_new_fdopen, _IO_fdopen)
172
173strong_alias (_IO_new_fdopen, __new_fdopen)
174versioned_symbol (libc, _IO_new_fdopen, _IO_fdopen, GLIBC_2_1);
175versioned_symbol (libc, __new_fdopen, fdopen, GLIBC_2_1);
176