michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Robin J. Maxwell 11-22-96 michael@0: * Fredrik Roubert 2010-07-23 michael@0: * Matt Austern 2010-07-23 michael@0: */ michael@0: michael@0: #include "prstrms.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: using std::ios_base; michael@0: using std::iostream; michael@0: using std::istream; michael@0: using std::nothrow; michael@0: using std::ostream; michael@0: using std::streambuf; michael@0: using std::streamsize; michael@0: michael@0: michael@0: PRfilebuf::PRfilebuf(): michael@0: _fd(NULL), michael@0: _opened(false), michael@0: _allocated(false), michael@0: _unbuffered(false), michael@0: _user_buf(false), michael@0: _buf_base(NULL), michael@0: _buf_end(NULL) { } michael@0: michael@0: michael@0: PRfilebuf::PRfilebuf(PRFileDesc *fd): michael@0: _fd(fd), michael@0: _opened(false), michael@0: _allocated(false), michael@0: _unbuffered(false), michael@0: _user_buf(false), michael@0: _buf_base(NULL), michael@0: _buf_end(NULL) { } michael@0: michael@0: michael@0: PRfilebuf::PRfilebuf(PRFileDesc *fd, char_type *ptr, streamsize len): michael@0: _fd(fd), michael@0: _opened(false), michael@0: _allocated(false), michael@0: _unbuffered(false), michael@0: _user_buf(false), michael@0: _buf_base(NULL), michael@0: _buf_end(NULL) michael@0: { michael@0: setbuf(ptr, len); michael@0: } michael@0: michael@0: michael@0: PRfilebuf::~PRfilebuf() michael@0: { michael@0: if (_opened) { michael@0: close(); michael@0: } else { michael@0: sync(); michael@0: } michael@0: if (_allocated) { michael@0: delete _buf_base; michael@0: } michael@0: } michael@0: michael@0: michael@0: PRfilebuf *PRfilebuf::open( michael@0: const char *name, ios_base::openmode flags, PRIntn mode) michael@0: { michael@0: if (_fd != NULL) { michael@0: return NULL; // Error if already open. michael@0: } michael@0: michael@0: // Translate flags argument. michael@0: PRIntn prflags = 0; michael@0: bool ate = (flags & ios_base::ate) != 0; michael@0: flags &= ~(ios_base::ate | ios_base::binary); michael@0: michael@0: // TODO: The flag PR_CREATE_FILE should probably be used for the cases michael@0: // (out), (out|app), (out|trunc) and (in|out|trunc) as the C++ standard michael@0: // specifies that these cases should open files 'as if by using fopen with michael@0: // "w"'. But adding that flag here will cause the unit test to leave files michael@0: // behind after running (which might or might not be an error in the unit michael@0: // test) so the matter needs further investigation before any changes are michael@0: // made. The old prstreams implementation used the non-standard flag michael@0: // ios::nocreate to control the use of PR_CREATE_FILE. michael@0: michael@0: if (flags == (ios_base::out)) { michael@0: prflags = PR_WRONLY | PR_TRUNCATE; michael@0: } else if (flags == (ios_base::out | ios_base::app)) { michael@0: prflags = PR_RDWR | PR_APPEND; michael@0: } else if (flags == (ios_base::out | ios_base::trunc)) { michael@0: prflags = PR_WRONLY | PR_TRUNCATE; michael@0: } else if (flags == (ios_base::in)) { michael@0: prflags = PR_RDONLY; michael@0: } else if (flags == (ios_base::in | ios_base::out)) { michael@0: prflags = PR_RDWR; michael@0: } else if (flags == (ios_base::in | ios_base::out | ios_base::trunc)) { michael@0: prflags = PR_RDWR | PR_TRUNCATE; michael@0: } else { michael@0: return NULL; // Unrecognized flag combination. michael@0: } michael@0: michael@0: if ((_fd = PR_Open(name, prflags, mode)) == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: _opened = true; michael@0: michael@0: if (ate && michael@0: seekoff(0, ios_base::end, flags) == pos_type(traits_type::eof())) { michael@0: close(); michael@0: return NULL; michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: michael@0: PRfilebuf *PRfilebuf::attach(PRFileDesc *fd) michael@0: { michael@0: if (_fd != NULL) { michael@0: return NULL; // Error if already open. michael@0: } michael@0: michael@0: _opened = false; michael@0: _fd = fd; michael@0: return this; michael@0: } michael@0: michael@0: michael@0: PRfilebuf *PRfilebuf::close() michael@0: { michael@0: if (_fd == NULL) michael@0: return NULL; michael@0: michael@0: int status = sync(); michael@0: michael@0: if (PR_Close(_fd) == PR_FAILURE || michael@0: traits_type::eq_int_type(status, traits_type::eof())) { michael@0: return NULL; michael@0: } michael@0: michael@0: _fd = NULL; michael@0: return this; michael@0: } michael@0: michael@0: michael@0: streambuf *PRfilebuf::setbuf(char_type *ptr, streamsize len) michael@0: { michael@0: if (is_open() && _buf_end) { michael@0: return NULL; michael@0: } michael@0: michael@0: if (!ptr || len <= 0) { michael@0: _unbuffered = true; michael@0: } else { michael@0: setb(ptr, ptr + len, false); michael@0: } michael@0: michael@0: return this; michael@0: } michael@0: michael@0: michael@0: streambuf::pos_type PRfilebuf::seekoff( michael@0: off_type offset, ios_base::seekdir dir, ios_base::openmode /*flags*/) michael@0: { michael@0: if (PR_GetDescType(_fd) != PR_DESC_FILE) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: PRSeekWhence whence; michael@0: PRInt64 pos; michael@0: michael@0: switch (dir) { michael@0: case ios_base::beg: whence = PR_SEEK_SET; break; michael@0: case ios_base::cur: whence = PR_SEEK_CUR; break; michael@0: case ios_base::end: whence = PR_SEEK_END; break; michael@0: default: michael@0: return traits_type::eof(); // This should never happen. michael@0: } michael@0: michael@0: if (traits_type::eq_int_type(sync(), traits_type::eof())) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: if ((pos = PR_Seek64(_fd, offset, whence)) == -1) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: return pos; michael@0: } michael@0: michael@0: michael@0: int PRfilebuf::sync() michael@0: { michael@0: if (_fd == NULL) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: if (!_unbuffered) { michael@0: // Sync write area. michael@0: PRInt32 waiting; michael@0: if ((waiting = pptr() - pbase()) != 0) { michael@0: PRInt32 nout; michael@0: if ((nout = PR_Write(_fd, pbase(), waiting)) != waiting) { michael@0: if (nout > 0) { michael@0: // Should set _pptr -= nout. michael@0: pbump(-nout); michael@0: memmove(pbase(), pbase() + nout, waiting - nout); michael@0: } michael@0: return traits_type::eof(); michael@0: } michael@0: } michael@0: setp(NULL, NULL); // Empty put area. michael@0: michael@0: if (PR_GetDescType(_fd) == PR_DESC_FILE) { michael@0: // Sockets can't seek; don't need this. michael@0: PROffset64 avail; michael@0: if ((avail = in_avail()) > 0) { michael@0: if (PR_Seek64(_fd, -avail, PR_SEEK_CUR) != -1) { michael@0: return traits_type::eof(); michael@0: } michael@0: } michael@0: } michael@0: setg(NULL, NULL, NULL); // Empty get area. michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: streambuf::int_type PRfilebuf::underflow() michael@0: { michael@0: PRInt32 count; michael@0: char_type byte; michael@0: michael@0: if (gptr() != NULL && gptr() < egptr()) { michael@0: return traits_type::to_int_type(*gptr()); michael@0: } michael@0: michael@0: // Make sure there is a reserve area. michael@0: if (!_unbuffered && _buf_base == NULL && !allocate()) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: // Sync before new buffer created below. michael@0: if (traits_type::eq_int_type(sync(), traits_type::eof())) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: if (_unbuffered) { michael@0: if (PR_Read(_fd, &byte, 1) <= 0) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: return traits_type::to_int_type(byte); michael@0: } michael@0: michael@0: if ((count = PR_Read(_fd, _buf_base, _buf_end - _buf_base)) <= 0) { michael@0: return traits_type::eof(); // Reached EOF. michael@0: } michael@0: michael@0: setg(_buf_base, _buf_base, _buf_base + count); michael@0: return traits_type::to_int_type(*gptr()); michael@0: } michael@0: michael@0: michael@0: streambuf::int_type PRfilebuf::overflow(int_type c) michael@0: { michael@0: // Make sure there is a reserve area. michael@0: if (!_unbuffered && _buf_base == NULL && !allocate()) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: // Sync before new buffer created below. michael@0: if (traits_type::eq_int_type(sync(), traits_type::eof())) { michael@0: return traits_type::eof(); michael@0: } michael@0: michael@0: if (!_unbuffered) { michael@0: setp(_buf_base, _buf_end); michael@0: } michael@0: michael@0: if (!traits_type::eq_int_type(c, traits_type::eof())) { michael@0: // Extract the byte to be written. michael@0: // (Required on big-endian architectures.) michael@0: char_type byte = traits_type::to_char_type(c); michael@0: if (!_unbuffered && pptr() < epptr()) { // Guard against recursion. michael@0: return sputc(byte); michael@0: } else { michael@0: if (PR_Write(_fd, &byte, 1) != 1) { michael@0: return traits_type::eof(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return traits_type::not_eof(c); michael@0: } michael@0: michael@0: michael@0: bool PRfilebuf::allocate() michael@0: { michael@0: char_type *buf = new(nothrow) char_type[BUFSIZ]; michael@0: if (buf == NULL) { michael@0: return false; michael@0: } michael@0: michael@0: setb(buf, buf + BUFSIZ, true); michael@0: return true; michael@0: } michael@0: michael@0: michael@0: void PRfilebuf::setb(char_type *buf_base, char_type *buf_end, bool user_buf) michael@0: { michael@0: if (_buf_base && !_user_buf) { michael@0: delete[] _buf_base; michael@0: } michael@0: michael@0: _buf_base = buf_base; michael@0: _buf_end = buf_end; michael@0: _user_buf = user_buf; michael@0: } michael@0: michael@0: michael@0: PRifstream::PRifstream(): michael@0: istream(NULL), michael@0: _filebuf() michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRifstream::PRifstream(PRFileDesc *fd): michael@0: istream(NULL), michael@0: _filebuf(fd) michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRifstream::PRifstream(PRFileDesc *fd, char_type *ptr, streamsize len): michael@0: istream(NULL), michael@0: _filebuf(fd, ptr, len) michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRifstream::PRifstream(const char *name, openmode flags, PRIntn mode): michael@0: istream(NULL), michael@0: _filebuf() michael@0: { michael@0: init(&_filebuf); michael@0: if (!_filebuf.open(name, flags | in, mode)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: PRifstream::~PRifstream() { } michael@0: michael@0: michael@0: void PRifstream::open(const char *name, openmode flags, PRIntn mode) michael@0: { michael@0: if (is_open() || !_filebuf.open(name, flags | in, mode)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: void PRifstream::attach(PRFileDesc *fd) michael@0: { michael@0: if (!_filebuf.attach(fd)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: void PRifstream::close() michael@0: { michael@0: if (_filebuf.close() == NULL) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: PRofstream::PRofstream(): michael@0: ostream(NULL), michael@0: _filebuf() michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRofstream::PRofstream(PRFileDesc *fd): michael@0: ostream(NULL), michael@0: _filebuf(fd) michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRofstream::PRofstream(PRFileDesc *fd, char_type *ptr, streamsize len): michael@0: ostream(NULL), michael@0: _filebuf(fd, ptr, len) michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRofstream::PRofstream(const char *name, openmode flags, PRIntn mode): michael@0: ostream(NULL), michael@0: _filebuf() michael@0: { michael@0: init(&_filebuf); michael@0: if (!_filebuf.open(name, flags | out, mode)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: PRofstream::~PRofstream() { } michael@0: michael@0: michael@0: void PRofstream::open(const char *name, openmode flags, PRIntn mode) michael@0: { michael@0: if (is_open() || !_filebuf.open(name, flags | out, mode)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: void PRofstream::attach(PRFileDesc *fd) michael@0: { michael@0: if (!_filebuf.attach(fd)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: void PRofstream::close() michael@0: { michael@0: if (_filebuf.close() == NULL) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: PRfstream::PRfstream(): michael@0: iostream(NULL), michael@0: _filebuf() michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRfstream::PRfstream(PRFileDesc *fd): michael@0: iostream(NULL), michael@0: _filebuf(fd) michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRfstream::PRfstream(PRFileDesc *fd, char_type *ptr, streamsize len): michael@0: iostream(NULL), michael@0: _filebuf(fd, ptr, len) michael@0: { michael@0: init(&_filebuf); michael@0: } michael@0: michael@0: michael@0: PRfstream::PRfstream(const char *name, openmode flags, PRIntn mode): michael@0: iostream(NULL), michael@0: _filebuf() michael@0: { michael@0: init(&_filebuf); michael@0: if (!_filebuf.open(name, flags | in | out, mode)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: PRfstream::~PRfstream() { } michael@0: michael@0: michael@0: void PRfstream::open(const char *name, openmode flags, PRIntn mode) michael@0: { michael@0: if (is_open() || !_filebuf.open(name, flags | in | out, mode)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: void PRfstream::attach(PRFileDesc *fd) michael@0: { michael@0: if (!_filebuf.attach(fd)) { michael@0: setstate(failbit); michael@0: } michael@0: } michael@0: michael@0: michael@0: void PRfstream::close() michael@0: { michael@0: if (_filebuf.close() == NULL) { michael@0: setstate(failbit); michael@0: } michael@0: }