1/*
2 * Copyright (c) 1999
3 * Silicon Graphics Computer Systems, Inc.
4 *
5 * Copyright (c) 1999
6 * Boris Fomitchev
7 *
8 * This material is provided "as is", with absolutely no warranty expressed
9 * or implied. Any use is at your own risk.
10 *
11 * Permission to use or copy this software for any purpose is hereby granted
12 * without fee, provided the above notices are retained on all copies.
13 * Permission to modify the code and to distribute modified code is granted,
14 * provided the above notices are retained, and a notice that the code was
15 * modified is included with the above copyright notice.
16 *
17 */
18
19#if defined  (__SUNPPRO_CC)  && !defined (_STLP_NO_NEW_C_HEADERS)
20#  include <time.h>
21// For sunpro, it chokes if time.h is included through stat.h
22#endif
23
24#include <fstream>
25
26#ifdef __CYGWIN__
27#  define __int64 long long
28#endif
29
30extern "C" {
31// open/close/read/write
32#include <sys/stat.h>           // For stat
33#if !defined (_CRAY) && ! defined (__EMX__)
34#  include <sys/mman.h>           // For mmap
35#endif
36
37//  on HP-UX 11, this one contradicts with pthread.h on pthread_atfork, unless we unset this
38#if defined (__hpux) && defined (__GNUC__)
39#  undef _INCLUDE_POSIX1C_SOURCE
40#endif
41
42#include <unistd.h>
43#include <fcntl.h>
44}
45
46#ifdef __APPLE__
47#  include <sys/sysctl.h>
48#endif
49
50const _STLP_fd INVALID_STLP_FD = -1;
51
52#ifndef O_ACCMODE
53#  define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
54#endif
55
56// Compare with streamoff definition in stl/char_traits.h!
57#if defined (_STLP_USE_DEFAULT_FILE_OFFSET) || \
58    (!defined(_LARGEFILE_SOURCE) && !defined (_LARGEFILE64_SOURCE))
59#  define FSTAT fstat
60#  define STAT  stat
61#  define LSEEK lseek
62#  define MMAP  mmap
63#  define OPEN  open
64#else
65#  define FSTAT fstat64
66#  define STAT  stat64
67#  define LSEEK lseek64
68#  define MMAP  mmap64
69#  define OPEN  open64
70#endif
71
72#ifndef MAP_FAILED /* MMAP failure return code */
73#  define MAP_FAILED -1
74#endif
75
76_STLP_BEGIN_NAMESPACE
77
78static ios_base::openmode flag_to_openmode(int mode)
79{
80  ios_base::openmode ret = ios_base::__default_mode;
81
82  switch ( mode & O_ACCMODE ) {
83    case O_RDONLY:
84      ret = ios_base::in;
85      break;
86    case O_WRONLY:
87      ret = ios_base::out;
88      break;
89    case O_RDWR:
90      ret = ios_base::in | ios_base::out;
91      break;
92  }
93
94  if ( mode & O_APPEND )
95    ret |= ios_base::app;
96
97  return ret;
98}
99
100_STLP_MOVE_TO_PRIV_NAMESPACE
101
102// Helper functions for _Filebuf_base.
103
104static bool __is_regular_file(_STLP_fd fd) {
105  struct STAT buf;
106  return FSTAT(fd, &buf) == 0 && S_ISREG(buf.st_mode);
107}
108
109// Number of characters in the file.
110static streamoff __file_size(_STLP_fd fd) {
111  streamoff ret = 0;
112
113  struct STAT buf;
114  if (FSTAT(fd, &buf) == 0 && S_ISREG(buf.st_mode))
115    ret = buf.st_size > 0 ? buf.st_size : 0;
116
117  return ret;
118}
119
120_STLP_MOVE_TO_STD_NAMESPACE
121
122size_t _Filebuf_base::_M_page_size = 4096;
123
124_Filebuf_base::_Filebuf_base()
125  : _M_file_id(INVALID_STLP_FD),
126    _M_openmode(0),
127    _M_is_open(false),
128    _M_should_close(false)
129{}
130
131void _Filebuf_base::_S_initialize()
132{
133#if defined (__APPLE__)
134  int mib[2];
135  size_t pagesize, len;
136  mib[0] = CTL_HW;
137  mib[1] = HW_PAGESIZE;
138  len = sizeof(pagesize);
139  sysctl(mib, 2, &pagesize, &len, NULL, 0);
140  _M_page_size = pagesize;
141#elif defined (__DJGPP) && defined (_CRAY)
142  _M_page_size = BUFSIZ;
143#else
144  _M_page_size = sysconf(_SC_PAGESIZE);
145#endif
146}
147
148// Return the size of the file.  This is a wrapper for stat.
149// Returns zero if the size cannot be determined or is ill-defined.
150streamoff _Filebuf_base::_M_file_size()
151{
152  return _STLP_PRIV __file_size(_M_file_id);
153}
154
155bool _Filebuf_base::_M_open(const char* name, ios_base::openmode openmode,
156                            long permission)
157{
158  _STLP_fd file_no;
159
160  if (_M_is_open)
161    return false;
162
163  int flags = 0;
164
165  // Unix makes no distinction between text and binary files.
166  switch ( openmode & (~ios_base::ate & ~ios_base::binary) ) {
167    case ios_base::out:
168    case ios_base::out | ios_base::trunc:
169      flags = O_WRONLY | O_CREAT | O_TRUNC;
170      break;
171    case ios_base::app:
172    case ios_base::out | ios_base::app:
173      flags = O_WRONLY | O_CREAT | O_APPEND;
174      break;
175    case ios_base::in:
176      flags = O_RDONLY;
177      permission = 0;             // Irrelevant unless we're writing.
178      break;
179    case ios_base::in | ios_base::out:
180      flags = O_RDWR;
181      break;
182    case ios_base::in | ios_base::out | ios_base::trunc:
183      flags = O_RDWR | O_CREAT | O_TRUNC;
184      break;
185    case ios_base::in | ios_base::app:
186    case ios_base::in | ios_base::out | ios_base::app:
187      flags = O_RDWR | O_CREAT | O_APPEND;
188      break;
189    default:                      // The above are the only combinations of
190      return false;               // flags allowed by the C++ standard.
191  }
192
193  file_no = OPEN(name, flags, permission);
194
195  if (file_no < 0)
196    return false;
197
198  _M_is_open = true;
199
200  if ((openmode & (ios_base::ate | ios_base::app)) && (LSEEK(file_no, 0, SEEK_END) == -1)) {
201    _M_is_open = false;
202  }
203
204  _M_file_id = file_no;
205  _M_should_close = _M_is_open;
206  _M_openmode = openmode;
207
208  if (_M_is_open)
209    _M_regular_file = _STLP_PRIV __is_regular_file(_M_file_id);
210
211  return (_M_is_open != 0);
212}
213
214
215bool _Filebuf_base::_M_open(const char* name, ios_base::openmode openmode)
216{
217  // This doesn't really grant everyone in the world read/write
218  // access.  On Unix, file-creation system calls always clear
219  // bits that are set in the umask from the permissions flag.
220  return this->_M_open(name, openmode, S_IRUSR | S_IWUSR | S_IRGRP |
221                                       S_IWGRP | S_IROTH | S_IWOTH);
222}
223
224// Associated the filebuf with a file descriptor pointing to an already-
225// open file.  Mode is set to be consistent with the way that the file
226// was opened.
227bool _Filebuf_base::_M_open(int file_no, ios_base::openmode)
228{
229  if (_M_is_open || file_no < 0)
230    return false;
231
232  int mode = fcntl(file_no, F_GETFL);
233
234  if (mode == -1)
235    return false;
236
237  _M_openmode = flag_to_openmode(mode);
238  _M_file_id = file_no;
239
240  _M_is_open = true;
241  _M_should_close = false;
242  _M_regular_file = _STLP_PRIV __is_regular_file(_M_file_id);
243  return true;
244}
245
246bool _Filebuf_base::_M_close()
247{
248  if (!_M_is_open)
249    return false;
250
251  bool ok = _M_should_close ? (close(_M_file_id) == 0) : true;
252
253  _M_is_open = _M_should_close = false;
254  _M_openmode = 0;
255  return ok;
256}
257
258// Read up to n characters into a buffer.  Return value is number of
259// characters read.
260ptrdiff_t _Filebuf_base::_M_read(char* buf, ptrdiff_t n)
261{
262  return read(_M_file_id, buf, n);
263}
264
265// Write n characters from a buffer.  Return value: true if we managed
266// to write the entire buffer, false if we didn't.
267bool _Filebuf_base::_M_write(char* buf, ptrdiff_t n)
268{
269  for (;;) {
270    ptrdiff_t written = write(_M_file_id, buf, n);
271
272    if (n == written) {
273      return true;
274    }
275
276    if (written > 0 && written < n) {
277      n -= written;
278      buf += written;
279    } else {
280      return false;
281    }
282  }
283}
284
285// Wrapper for lseek or the like.
286streamoff _Filebuf_base::_M_seek(streamoff offset, ios_base::seekdir dir)
287{
288  int whence;
289
290  switch ( dir ) {
291    case ios_base::beg:
292      if (offset < 0 /* || offset > _M_file_size() */ )
293        return streamoff(-1);
294      whence = SEEK_SET;
295      break;
296    case ios_base::cur:
297      whence = SEEK_CUR;
298      break;
299    case ios_base::end:
300      if (/* offset > 0 || */  -offset > _M_file_size() )
301        return streamoff(-1);
302      whence = SEEK_END;
303      break;
304    default:
305      return streamoff(-1);
306  }
307
308  return LSEEK(_M_file_id, offset, whence);
309}
310
311// Attempts to memory-map len bytes of the current file, starting
312// at position offset.  Precondition: offset is a multiple of the
313// page size.  Postcondition: return value is a null pointer if the
314// memory mapping failed.  Otherwise the return value is a pointer to
315// the memory-mapped file and the file position is set to offset.
316void* _Filebuf_base::_M_mmap(streamoff offset, streamoff len)
317{
318  void* base;
319#if !defined (__DJGPP) && !defined (_CRAY)
320  base = MMAP(0, len, PROT_READ, MAP_PRIVATE, _M_file_id, offset);
321  if (base != (void*)MAP_FAILED) {
322    if (LSEEK(_M_file_id, offset + len, SEEK_SET) < 0) {
323      this->_M_unmap(base, len);
324      base = 0;
325    }
326  } else
327    base =0;
328#else
329  _STLP_MARK_PARAMETER_AS_UNUSED(&offset)
330  _STLP_MARK_PARAMETER_AS_UNUSED(&len)
331  base = 0;
332#endif
333  return base;
334}
335
336void _Filebuf_base::_M_unmap(void* base, streamoff len)
337{
338  // precondition : there is a valid mapping at the moment
339#if !defined (__DJGPP) && !defined (_CRAY)
340  munmap((char*)base, len);
341#else
342  _STLP_MARK_PARAMETER_AS_UNUSED(&len)
343  _STLP_MARK_PARAMETER_AS_UNUSED(base)
344#endif
345}
346
347_STLP_END_NAMESPACE
348