NetBSD/gnu/lib/libg++/iostream/streambuf.C

667 lines
14 KiB
C

// This is part of the iostream library, providing input/output for C++.
// Copyright (C) 1991, 1992 Per Bothner.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#define _STREAM_COMPAT
#ifdef __GNUG__
#pragma implementation
#endif
#include "ioprivate.h"
#include <string.h>
void streambuf::_un_link()
{
if (_flags & _S_LINKED) {
streambuf **f;
for (f = &_list_all; *f != NULL; f = &(*f)->xchain()) {
if (*f == this) {
*f = xchain();
break;
}
}
_flags &= ~_S_LINKED;
}
}
void streambuf::_link_in()
{
if ((_flags & _S_LINKED) == 0) {
_flags |= _S_LINKED;
xchain() = _list_all;
_list_all = this;
}
}
// Return minimum _pos markers
// Assumes the current get area is the main get area.
int streambuf::_least_marker()
{
int least_so_far = _egptr - _eback;
for (register streammarker *mark = _markers;
mark != NULL; mark = mark->_next)
if (mark->_pos < least_so_far)
least_so_far = mark->_pos;
return least_so_far;
}
// Switch current get area from backup buffer to (start of) main get area.
void streambuf::switch_to_main_get_area()
{
char *tmp;
_flags &= ~_S_IN_BACKUP;
// Swap _egptr and _other_egptr.
tmp= _egptr; _egptr= _other_egptr; _other_egptr= tmp;
// Swap _eback and _other_gbase.
tmp= _eback; _eback = _other_gbase; _other_gbase = tmp;
_gptr = _eback;
}
// Switch current get area from main get area to (end of) backup area.
void streambuf::switch_to_backup_area()
{
char *tmp;
_flags |= _S_IN_BACKUP;
// Swap _egptr and _other_egptr.
tmp = _egptr; _egptr = _other_egptr; _other_egptr = tmp;
// Swap _gbase and _other_gbase.
tmp = _eback; _eback = _other_gbase; _other_gbase = tmp;
_gptr = _egptr;
}
int streambuf::switch_to_get_mode()
{
if (_pptr > _pbase)
if (overflow(EOF) == EOF)
return EOF;
if (in_backup()) {
_eback = _aux_limit;
}
else {
_eback = _base;
if (_pptr > _egptr)
_egptr = _pptr;
}
_gptr = _pptr;
setp(_gptr, _gptr);
_flags &= ~_S_CURRENTLY_PUTTING;
return 0;
}
void streambuf::free_backup_area()
{
if (in_backup())
switch_to_main_get_area(); // Just in case.
delete [] _other_gbase;
_other_gbase = NULL;
_other_egptr = NULL;
_aux_limit = NULL;
}
#if 0
int streambuf::switch_to_put_mode()
{
_pbase = _gptr;
_pptr = _gptr;
_epptr = in_backup() ? _egptr : _ebuf; // wrong if line- or un-buffered?
_gptr = _egptr;
_eback = _egptr;
_flags |= _S_CURRENTLY_PUTTING;
return 0;
}
#endif
#ifdef _G_FRIEND_BUG
int __underflow(register streambuf *sb) { return __UNDERFLOW(sb); }
int __UNDERFLOW(register streambuf *sb)
#else
int __underflow(register streambuf *sb)
#endif
{
if (sb->put_mode())
if (sb->switch_to_get_mode() == EOF) return EOF;
if (sb->_gptr < sb->_egptr)
return *(unsigned char*)sb->_gptr;
if (sb->in_backup()) {
sb->switch_to_main_get_area();
if (sb->_gptr < sb->_egptr)
return *sb->_gptr;
}
if (sb->have_markers()) {
// Append [_gbase.._egptr] to backup area.
int least_mark = sb->_least_marker();
// needed_size is how much space we need in the backup area.
int needed_size = (sb->_egptr - sb->_eback) - least_mark;
int current_Bsize = sb->_other_egptr - sb->_other_gbase;
int avail; // Extra space available for future expansion.
if (needed_size > current_Bsize) {
avail = 0; // 100 ?? FIXME
char *new_buffer = new char[avail+needed_size];
if (least_mark < 0) {
memcpy(new_buffer + avail,
sb->_other_egptr + least_mark,
-least_mark);
memcpy(new_buffer +avail - least_mark,
sb->_eback,
sb->_egptr - sb->_eback);
}
else
memcpy(new_buffer + avail,
sb->_eback + least_mark,
needed_size);
delete [] sb->_other_gbase;
sb->_other_gbase = new_buffer;
sb->_other_egptr = new_buffer + avail + needed_size;
}
else {
avail = current_Bsize - needed_size;
if (least_mark < 0) {
memmove(sb->_other_gbase + avail,
sb->_other_egptr + least_mark,
-least_mark);
memcpy(sb->_other_gbase + avail - least_mark,
sb->_eback,
sb->_egptr - sb->_eback);
}
else if (needed_size > 0)
memcpy(sb->_other_gbase + avail,
sb->_eback + least_mark,
needed_size);
}
// FIXME: Dubious arithmetic if pointers are NULL
sb->_aux_limit = sb->_other_gbase + avail;
// Adjust all the streammarkers.
int delta = sb->_egptr - sb->_eback;
for (register streammarker *mark = sb->_markers;
mark != NULL; mark = mark->_next)
mark->_pos -= delta;
}
else if (sb->have_backup())
sb->free_backup_area();
return sb->underflow();
}
#ifdef _G_FRIEND_BUG
int __overflow(register streambuf *sb, int c) { return __OVERFLOW(sb, c); }
int __OVERFLOW(register streambuf *sb, int c)
#else
int __overflow(streambuf* sb, int c)
#endif
{
return sb->overflow(c);
}
int streambuf::xsputn(register const char* s, int n)
{
if (n <= 0)
return 0;
register int more = n;
for (;;) {
int count = _epptr - _pptr; // Space available.
if (count > 0) {
if (count > more)
count = more;
if (count > 20) {
memcpy(_pptr, s, count);
s += count;
_pptr += count;
}
else if (count <= 0)
count = 0;
else {
register char *p = _pptr;
for (register int i = count; --i >= 0; ) *p++ = *s++;
_pptr = p;
}
more -= count;
}
if (more == 0 || __overflow(this, (unsigned char)*s++) == EOF)
break;
more--;
}
return n - more;
}
int streambuf::padn(char pad, int count)
{
#define PADSIZE 16
static char const blanks[PADSIZE] =
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
static char const zeroes[PADSIZE] =
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
char padbuf[PADSIZE];
const char *padptr;
register int i;
if (pad == ' ')
padptr = blanks;
else if (pad == '0')
padptr = zeroes;
else {
for (i = PADSIZE; --i >= 0; ) padbuf[i] = pad;
padptr = padbuf;
}
for (i = count; i >= PADSIZE; i -= PADSIZE)
if (sputn(padptr, PADSIZE) != PADSIZE)
return EOF;
if (i > 0 && sputn(padptr, i) != i)
return EOF;
return pad;
}
int streambuf::xsgetn(char* s, int n)
{
register int more = n;
for (;;) {
int count = _egptr - _gptr; // Data available.
if (count > 0) {
if (count > more)
count = more;
if (count > 20) {
memcpy(s, _gptr, count);
s += count;
_gptr += count;
}
else if (count <= 0)
count = 0;
else {
register char *p = _gptr;
for (register int i = count; --i >= 0; ) *s++ = *p++;
_gptr = p;
}
more -= count;
}
if (more == 0 || __underflow(this) == EOF)
break;
}
return n - more;
}
int streambuf::ignore(int n)
{
register int more = n;
for (;;) {
int count = _egptr - _gptr; // Data available.
if (count > 0) {
if (count > more)
count = more;
_gptr += count;
more -= count;
}
if (more == 0 || __underflow(this) == EOF)
break;
}
return n - more;
}
int streambuf::sync()
{
if (gptr() == egptr() && pptr() == pbase())
return 0;
return EOF;
}
int streambuf::pbackfail(int c)
{
if (_gptr > _eback)
_gptr--;
else if (seekoff(-1, ios::cur, ios::in) == EOF)
return EOF;
if (c != EOF && *_gptr != c)
*_gptr = c;
return (unsigned char)c;
}
streambuf* streambuf::setbuf(char* p, int len)
{
if (sync() == EOF)
return NULL;
if (p == NULL || len == 0) {
unbuffered(1);
setb(_shortbuf, _shortbuf+1, 0);
}
else {
unbuffered(0);
setb(p, p+len, 0);
}
setp(0, 0);
setg(0, 0, 0);
return this;
}
streampos streambuf::seekpos(streampos pos, int mode)
{
return seekoff(pos, ios::beg, mode);
}
void streambuf::setb(char* b, char* eb, int a)
{
if (_base && !(_flags & _S_USER_BUF))
FREE_BUF(_base);
_base = b;
_ebuf = eb;
if (a)
_flags &= ~_S_USER_BUF;
else
_flags |= _S_USER_BUF;
}
int streambuf::doallocate()
{
char *buf = ALLOC_BUF(_G_BUFSIZ);
if (buf == NULL)
return EOF;
setb(buf, buf+_G_BUFSIZ, 1);
return 1;
}
void streambuf::doallocbuf()
{
if (base() || (!unbuffered() && doallocate() != EOF)) return;
setb(_shortbuf, _shortbuf+1, 0);
}
streambuf::streambuf(int flags)
{
_flags = _IO_MAGIC|flags;
_base = NULL;
_ebuf = NULL;
_eback = NULL;
_gptr = NULL;
_egptr = NULL;
_pbase = NULL;
_pptr = NULL;
_epptr = NULL;
_chain = NULL; // Not necessary.
_other_gbase = NULL;
_aux_limit = NULL;
_other_egptr = NULL;
_markers = NULL;
_cur_column = 0;
}
streambuf::~streambuf()
{
if (_base && !(_flags & _S_USER_BUF))
FREE_BUF(_base);
for (register streammarker *mark = _markers;
mark != NULL; mark = mark->_next)
mark->_sbuf = NULL;
}
streampos
streambuf::seekoff(streamoff, _seek_dir, int mode /*=ios::in|ios::out*/)
{
return EOF;
}
int streambuf::sputbackc(char c)
{
if (_gptr > _eback && (unsigned char)_gptr[-1] == (unsigned char)c) {
_gptr--;
return (unsigned char)c;
}
return pbackfail(c);
}
int streambuf::sungetc()
{
if (_gptr > _eback) {
_gptr--;
return (unsigned char)*_gptr;
}
else
return pbackfail(EOF);
}
#if 0 /* Work in progress */
void streambuf::collumn(int c)
{
if (c == -1)
_collumn = -1;
else
_collumn = c - (_pptr - _pbase);
}
#endif
int streambuf::get_column()
{
if (_cur_column)
return __adjust_column(_cur_column - 1, pbase(), pptr() - pbase());
return -1;
}
int streambuf::set_column(int i)
{
_cur_column = i+1;
return 0;
}
int streambuf::flush_all()
{
int result = 0;
for (streambuf *sb = _list_all; sb != NULL; sb = sb->xchain())
if (sb->overflow(EOF) == EOF)
result = EOF;
return result;
}
void streambuf::flush_all_linebuffered()
{
for (streambuf *sb = _list_all; sb != NULL; sb = sb->xchain())
if (sb->linebuffered())
sb->overflow(EOF);
}
int backupbuf::underflow()
{
return EOF;
}
int backupbuf::overflow(int c)
{
return EOF;
}
streammarker::streammarker(streambuf *sb)
{
_sbuf = sb;
if (!(sb->xflags() & _S_IS_BACKUPBUF)) {
set_streampos(sb->seekoff(0, ios::cur, ios::in));
_next = 0;
}
else {
if (sb->put_mode())
sb->switch_to_get_mode();
if (((backupbuf*)sb)->in_backup())
set_offset(sb->_gptr - sb->_egptr);
else
set_offset(sb->_gptr - sb->_eback);
// Should perhaps sort the chain?
_next = ((backupbuf*)sb)->_markers;
((backupbuf*)sb)->_markers = this;
}
}
streammarker::~streammarker()
{
if (saving()) {
// Unlink from sb's chain.
register streammarker **ptr = &((backupbuf*)_sbuf)->_markers;
for (; ; ptr = &(*ptr)->_next)
if (*ptr == NULL)
break;
else if (*ptr == this) {
*ptr = _next;
return;
}
}
#if 0
if _sbuf has a backup area that is no longer needed, should we delete
it now, or wait until underflow()?
#endif
}
#define BAD_DELTA EOF
int streammarker::delta(streammarker& other_mark)
{
if (_sbuf != other_mark._sbuf)
return BAD_DELTA;
if (saving() && other_mark.saving())
return _pos - other_mark._pos;
else if (!saving() && !other_mark.saving())
return _spos - other_mark._spos;
else
return BAD_DELTA;
}
int streammarker::delta()
{
if (_sbuf == NULL)
return BAD_DELTA;
if (saving()) {
int cur_pos;
if (_sbuf->in_backup())
cur_pos = _sbuf->_gptr - _sbuf->_egptr;
else
cur_pos = _sbuf->_gptr - _sbuf->_eback;
return _pos - cur_pos;
}
else {
if (_spos == EOF)
return BAD_DELTA;
int cur_pos = _sbuf->seekoff(0, ios::cur);
if (cur_pos == EOF)
return BAD_DELTA;
return _pos - cur_pos;
}
}
int streambuf::seekmark(streammarker& mark, int delta /* = 0 */)
{
if (mark._sbuf != this)
return EOF;
if (!mark.saving()) {
return seekpos(mark._spos, ios::in);
}
else if (mark._pos >= 0) {
if (in_backup())
switch_to_main_get_area();
_gptr = _eback + mark._pos;
}
else {
if (!in_backup())
switch_to_backup_area();
_gptr = _egptr + mark._pos;
}
return 0;
}
void streambuf::unsave_markers()
{
register streammarker *mark =_markers;
if (_markers) {
streampos offset = seekoff(0, ios::cur, ios::in);
if (offset != EOF) {
offset += eGptr() - Gbase();
for ( ; mark != NULL; mark = mark->_next)
mark->set_streampos(mark->_pos + offset);
}
else {
for ( ; mark != NULL; mark = mark->_next)
mark->set_streampos(EOF);
}
_markers = 0;
}
free_backup_area();
}
int backupbuf::pbackfail(int c)
{
if (_gptr <= _eback) {
// Need to handle a filebuf in write mode (switch to read mode). FIXME!
if (have_backup() && !in_backup()) {
switch_to_backup_area();
}
if (!have_backup()) {
// No backup buffer: allocate one.
// Use short buffer, if unused? (probably not) FIXME
int backup_size = 128;
_other_gbase = new char [backup_size];
_other_egptr = _other_gbase + backup_size;
_aux_limit = _other_egptr;
switch_to_backup_area();
}
else if (gptr() <= eback()) {
// Increase size of existing backup buffer.
size_t new_size;
size_t old_size = egptr() - eback();
new_size = 2 * old_size;
char* new_buf = new char [new_size];
memcpy(new_buf+(new_size-old_size), eback(), old_size);
delete [] eback();
setg(new_buf, new_buf+(new_size-old_size), new_buf+new_size);
_aux_limit = _gptr;
}
}
_gptr--;
if (c != EOF && *_gptr != c)
*_gptr = c;
return (unsigned char)*_gptr;
}
unsigned __adjust_column(unsigned start, const char *line, int count)
{
register const char *ptr = line + count;
while (ptr > line)
if (*--ptr == '\n')
return line + count - ptr - 1;
return start + count;
}
int ios::readable() { return !(rdbuf()->_flags & _S_NO_READS); }
int ios::writable() { return !(rdbuf()->_flags & _S_NO_WRITES); }
int ios::is_open() { return rdbuf()
&& (rdbuf()->_flags & _S_NO_READS+_S_NO_WRITES)
!= _S_NO_READS+_S_NO_WRITES; }
#if defined(linux)
#define IO_CLEANUP ;
#endif
#ifdef IO_CLEANUP
IO_CLEANUP
#else
struct __io_defs {
__io_defs() { }
~__io_defs() { streambuf::flush_all(); }
};
__io_defs io_defs__;
#endif