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