summaryrefslogtreecommitdiff
path: root/lib/prstreams/prstrms.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/prstreams/prstrms.cpp')
-rw-r--r--lib/prstreams/prstrms.cpp548
1 files changed, 548 insertions, 0 deletions
diff --git a/lib/prstreams/prstrms.cpp b/lib/prstreams/prstrms.cpp
new file mode 100644
index 0000000..3dbe3f4
--- /dev/null
+++ b/lib/prstreams/prstrms.cpp
@@ -0,0 +1,548 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * 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);
+ }
+}