pjs/nsprpub/lib/prstreams/prstrms.cpp

549 строки
12 KiB
C++

/* -*- 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);
}
}